module Language.Mecha.Solid
  ( Solid
  , sphere
  , cone
  , box
  , cube
  , cylinder
  , tube
  ) where

import Language.Mecha.Types
import Text.Printf

data Solid
  = Primitive String String [Transform] Color
  | Union        Solid Solid
  | Intersection Solid Solid
  | Difference   Solid Solid
  deriving Eq

data Transform
  = Scale (Vector)
  | Move  (Vector)
  | RotateX Double
  | RotateY Double
  | RotateZ Double
  deriving Eq

transform :: Transform -> Solid -> Solid
transform t a = case a of
  Primitive    a b c d -> Primitive a b (c ++ [t]) d
  Union        a b     -> Union         (transform t a) (transform t b)
  Intersection a b     -> Intersection  (transform t a) (transform t b)
  Difference   a b     -> Difference    (transform t a) (transform t b)

instance Moveable Solid where
  move a    = transform $ Move a
  rotateX a = transform $ RotateX a
  rotateY a = transform $ RotateY a
  rotateZ a = transform $ RotateZ a

instance Scaleable Solid where
  scale a   = transform $ Scale a

instance Setable Solid where
  union        = Union
  intersection = Intersection
  difference   = Difference

instance Colorable Solid where
  color c a = case a of
    Primitive    a b t _ -> Primitive a b t c
    Union        a b     -> Union         (color c a) (color c b)
    Intersection a b     -> Intersection  (color c a) (color c b)
    Difference   a b     -> Difference    (color c a) (color c b)

instance Show Solid where
  show a = case a of
    Primitive a1 a2 t (r, g, b, o) -> printf "%s { %s\n%s%s}\n" a1 a2 trans color
      where
      color :: String
      color = printf "  pigment { rgbt <%f, %f, %f, %f> }\n" r g b o
      trans :: String
      trans = concatMap show t
    Union        a b   -> printf "merge        {\n%s%s}\n" (show a) (show b)
    Intersection a b   -> printf "intersection {\n%s%s}\n" (show a) (show b)
    Difference   a b   -> printf "difference   {\n%s%s}\n" (show a) (show b)

instance Show Transform where
  show a = case a of
    Scale (x, y, z) -> printf "  scale <%f, %f, %f>\n" x z y
    Move  (x, y, z) -> printf "  translate <%f, %f, %f>\n" x z y
    RotateX a       -> printf "  rotate <%f, 0, 0>\n" (-a * 180 / pi)
    RotateY a       -> printf "  rotate <0, 0, %f>\n" (-a * 180 / pi)
    RotateZ a       -> printf "  rotate <0, %f, 0>\n" (-a * 180 / pi)


primitive :: String -> String -> Solid
primitive a b = Primitive a b [] (0.5, 0.5, 0.5, 0)

-- | A sphere with diameter centered at origin.
sphere :: Double -> Solid
sphere d = primitive "sphere" $ printf "<0, 0, 0>, %f" (d / 2)

-- | A cube with edge length centered at origin.
cube :: Double -> Solid
cube a = box c c c
  where
  b = a / 2
  c = (-b, b)

-- | A cone with base at the origin, given base diameter, top diameter, and height.
cone :: Double -> Double -> Double -> Solid
cone bd td h = primitive "cone" $ printf "<0, 0, 0>, %f <0, %f, 0>, %f" (bd / 2) h (td / 2)

-- | A cylinder with base at the origin, given diameter and height.
cylinder :: Double -> Double -> Solid
cylinder d h = cone d d h

-- | A hollow cylinder with base at the origin, given outer diameter, inner diamter, and height.
tube od id h = difference (cylinder od h) (moveZ (-h) $ cylinder id (4 * h))

-- | A box with ranges or X, Y, and Z positions.
box :: (Double, Double) -> (Double, Double) -> (Double, Double) -> Solid
box (x1, x2) (y1, y2) (z1, z2) = primitive "box" $ printf "<%f, %f, %f>, <%f, %f, %f>" x1 z1 y1 x2 z2 y2