{-| GCode types

This module exports types for constructing 'Code' values

-}

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleInstances #-}
module Data.GCode.Types (
      Class(..)
    , AxisDesignator(..)
    , ParamDesignator(..)
    , Axes
    , Params
    , Limits
    , ParamLimits
    , Code(..)
    , GCode
    , codecls
    , axis
    , axis'
    , param
    , param'
    , CodeMod
    , cls
    , num
    , sub
    , axes
    , params
    , comment
    , appmod
    , eval
    , emptyCode
    , defaultPrec
    , Style(..)
    , defaultStyle
    ) where

import qualified Data.ByteString      as B
import qualified Data.Text            as T
import qualified Data.Text.Encoding   as TE

--import qualified Foldable as F
import Data.Semigroup hiding (option)
import Control.Monad.State.Strict
import Control.Applicative

import qualified Data.Map.Strict as M



-- | Code class
data Class =
    G   -- ^ G-code
  | M   -- ^ M-code
  | T   -- ^ T-code (temperature)
  | StP -- ^ Stand-alone P-code
  | StF -- ^ Stand-alone F-code
  | StS -- ^ Stand-alone S-code
  deriving (Show, Enum, Eq, Ord)

-- | Axis letter
data AxisDesignator =
    X -- ^ X-axis
  | Y -- ^ Y-axis
  | Z -- ^ Z-axis
  | A -- ^ A-axis
  | B -- ^ B-axis
  | C -- ^ C-axis
  | E -- ^ Extruder axis
  | L
  deriving (Show, Enum, Eq, Ord)

-- | Param letter
data ParamDesignator =
    S -- ^ S parameter - usually spindle RPM
  | P -- ^ P parameter
  | F -- ^ F parameter - usually feedrate
  | R -- ^ R parameter
  deriving (Show, Enum, Eq, Ord)

-- |Convert 'Char' representation of a code to its 'Class'
codecls :: Char -> Class
codecls 'G' = G
codecls 'M' = M
codecls 'T' = T
codecls 'P' = StP
codecls 'F' = StF
codecls 'S' = StS

-- |Convert 'Char' representation of an axis to its 'AxisDesignator'
axis :: Char -> AxisDesignator
axis 'X' = X
axis 'Y' = Y
axis 'Z' = Z
axis 'A' = A
axis 'B' = B
axis 'C' = C
axis 'E' = E
axis 'L' = L

-- |Convert 'Char' representation of a param to its 'ParamDesignator'
param :: Char -> ParamDesignator
param 'S' = S
param 'P' = P
param 'F' = F
param 'R' = R

-- | Map of 'AxisDesignator' to 'Double'
type Axes = M.Map AxisDesignator Double

type Limits = M.Map AxisDesignator (Double, Double)

-- | Map of 'ParamDesignator' to 'Double'
type Params = M.Map ParamDesignator Double

type ParamLimits = M.Map ParamDesignator (Double, Double)

-- | List of 'Code's
type GCode = [Code]

data Code =
    Code {
        codeCls :: Maybe Class            -- ^ Code 'Class' (M in M5)
      , codeNum :: Maybe Int             -- ^ Code value (81 in G81)
      , codeSub :: Maybe Int              -- ^ Code subcode (1 in G92.1)
      , codeAxes :: Axes            -- ^ Code 'Axes'
      , codeParams :: Params        -- ^ Code 'Params'
      , codeComment :: B.ByteString -- ^ Comment following this Code
    }
  | Comment B.ByteString        -- ^ Standalone comment
  | Empty                       -- ^ Empty lines
  | Other B.ByteString          -- ^ Parser unhandled lines
  deriving (Show, Eq, Ord)

newtype CodeMod = CodeMod
  { applyCodeMod :: Code -> Code }

instance Monoid CodeMod where
  mempty = CodeMod id
  mappend = (<>)

instance Semigroup CodeMod where
  m1 <> m2 = CodeMod $ applyCodeMod m1 . applyCodeMod m2

cls :: Class -> CodeMod
cls x = CodeMod $ \c -> c { codeCls = Just x}

num :: Int -> CodeMod
num x = CodeMod $ \c -> c { codeNum = Just x}

sub :: Int -> CodeMod
sub x = CodeMod $ \c -> c { codeSub = Just x}

axes :: Axes -> CodeMod
axes x = CodeMod $ \c -> c { codeAxes = x}

axis' :: AxisDesignator -> Double -> CodeMod
axis' des val = CodeMod $ \c -> c { codeAxes = M.insert des val $ codeAxes c }

params :: Params -> CodeMod
params x = CodeMod $ \c -> c { codeParams = x}

param' :: ParamDesignator -> Double -> CodeMod
param' des val = CodeMod $ \c -> c { codeParams = M.insert des val $ codeParams c }

comment :: B.ByteString -> CodeMod
comment x = CodeMod $ \c -> c { codeComment = x}

appmod :: CodeMod -> Code -> Code
appmod m c = applyCodeMod m c

--data Sim = Empty
--  | Line Axes Axes
--  deriving (Show, Eq)

--eval c1 c2 = Line (codeAxes c1) (codeAxes c2)
eval = undefined

emptyCode = Code Nothing Nothing Nothing M.empty M.empty ""



data Style =
  Style {
      stylePrecision :: Int
    , styleColorful :: Bool
  } deriving (Show)

defaultPrec :: Int
defaultPrec = 6

defaultStyle = Style defaultPrec False