{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE DuplicateRecordFields #-}

module Walker.SCAD where

import           Data.Makefile
import           Data.Makefile.Render
import qualified Data.Text                     as Text
import           SCAD
import           Language.OpenSCAD
import           Shared
import           System.Process
import           Walker
import           Data.List.Unique
import           System.Directory                         (createDirectoryIfMissing
                                                          )

-- both are not necessary in the PKG
-- A. generate skeletons
-- B. SCAD files

poseToSCAD :: Pose -> [Object] -> Object
poseToSCAD pose children =
  translate (pos pose) $ qrotate (ori pose) $ union children


modelToSCAD :: ExtendedSCADModel -> [TopLevel]
modelToSCAD ExtendedSCADModel { absolute_imports = imports, smodel = SCADModel { module_name, args } }
  = (UseDirective <$> imports)
    ++ [TopLevelScope $ Module (Ident module_name) (args) Nothing]

getSCADFiles :: ExtendedWalker -> [(Filename, [TopLevel])]
getSCADFiles = mapModels (\x -> (scad_filename x, modelToSCAD x))

getSCADSkeleton :: ExtendedWalker -> (ExtendedAssembly -> Object) -> [TopLevel]
getSCADSkeleton walker ass_to_skeleton =
  (UseDirective <$> imports)
    ++ [ TopLevelScope $ union $ concat (leg_skeleton <$> legs walker)
       , TopLevelScope
       $ ass_to_skeleton
       $ body_ass
       $ base_body
       $ body walker
       ]
 where
  imports = sortUniq $ concat $ mapModels absolute_imports walker

  leg_skeleton :: ExtendedLeg -> [Object]
  leg_skeleton Leg { root_joint } = foldJoints fold_joint [] root_joint

  fold_joint :: ExtendedJoint -> [Object] -> [Object]
  fold_joint ExtendedJoint { joint } sub = [poseToSCAD (offset joint) (content $ link $ sub_link joint)]
   where
    map_ass :: Maybe ExtendedAssembly -> [Object]
    map_ass Nothing = []
    map_ass (Just assemb) = [ass_to_skeleton assemb]

    content :: Link ExtendedAssembly -> [Object]
    content lin = case jtype joint of
      RevoluteJoint { axis, default_angle } ->
        [ rotate axis default_angle
            $ union
            $ ((map_ass $ assembly lin) ++ sub)
        ]
      FixedJoint -> (map_ass $ assembly lin) ++ sub

assemblyToVisual :: ExtendedAssembly -> Object
assemblyToVisual Assembly {amodel} = modelToVisual amodel

assemblyToCollision :: ExtendedAssembly -> Object
assemblyToCollision Assembly {collision_models} = 
  union
    $   (\ExtendedSCADModel { smodel = SCADModel { module_name, args } } ->
          Module (Ident module_name) args Nothing
        )
    <$> collision_models

modelToVisual :: ExtendedSCADModel -> Object
modelToVisual ExtendedSCADModel{smodel = SCADModel {module_name, args}} = Module (Ident module_name) args Nothing


getSCADVisualSkeleton :: ExtendedWalker -> [TopLevel]
getSCADVisualSkeleton walkie = getSCADSkeleton walkie assemblyToVisual

getSCADCollisionSkeleton :: ExtendedWalker -> [TopLevel]
getSCADCollisionSkeleton walkie = getSCADSkeleton walkie assemblyToCollision


getSTLRenderList :: ExtendedWalker -> [(Filename, Filename)]
getSTLRenderList = mapModels model_to_render
 where
  model_to_render :: ExtendedSCADModel -> (Filename, Filename)
  model_to_render ExtendedSCADModel { scad_filename, stl_filename } =
    (scad_filename, stl_filename)


getMakefileRenderer :: ExtendedWalker -> Makefile
getMakefileRenderer walker = Makefile
  { entries = Rule (Target $ Text.pack "all")
                   (list_to_deb <$> getSTLRenderList walker)
                   []
                : (list_to_rule <$> getSTLRenderList walker)
  }
 where
  list_to_rule :: (Filename, Filename) -> Entry
  list_to_rule (scad_file, stl_file) = Rule
    (Target $ Text.pack stl_file)
    [Dependency $ Text.pack scad_file]
    (stlRenderCommands scad_file stl_file)

  list_to_deb (_, stl_file) = Dependency $ Text.pack stl_file

renderWalkerStl :: ExtendedWalker -> IO ()
renderWalkerStl walker = do
  createDirectoryIfMissing False ("/tmp/schpin_robot_lib/")
  writeMakefile "/tmp/schpin_robot_lib/Makefile" $ getMakefileRenderer walker
  callCommand "cd /tmp/schpin_robot_lib/ && make -j 4"
