{-# LANGUAGE DeriveFunctor     #-}
{-# LANGUAGE FlexibleContexts  #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards   #-}
{-# LANGUAGE TemplateHaskell   #-}

module Docker.Types where

import           Control.Applicative
import           Control.Lens.TH
import           Control.Lens.TH
import           Data.Aeson
import           Data.Aeson.TH
import           Data.Bool
import qualified Data.ByteString.Lazy as BS
import           Data.Default
import qualified Data.Map             as Map
import qualified Data.Text            as T
import           Docker.Options
import           GHC.Generics
import           Network.Wreq.Types   (Postable)


type URL = String
type ApiVersion = String
type Endpoint = String

type Tag = String
type IP = String
type Port = Int
type PortType = String

data DockerClientOpts = DockerClientOpts {
      apiVersion :: ApiVersion
    , baseUrl    :: URL
    } deriving (Show)


data ResourceId = ResourceId { _id :: String } deriving (Show, Eq)


data DockerImage = DockerImage
                { _imageId        :: ResourceId
                , _imageCreatedAt :: Int
                , _parentId       :: Maybe String
                , _repoTags       :: [Tag]
                , _size           :: Int
                , _virtualSize    :: Int
                } deriving (Show, Eq)


data DockerVersion = DockerVersion
                  { _Version       :: String
                  , _GitCommit     :: String
                  , _GoVersion     :: String
                  , _Arch          :: String
                  , _KernelVersion :: String
                  } deriving (Show, Eq)


-- The JSON looks likes this:
-- "Ports":[{"IP":"0.0.0.0","PrivatePort":55555,"PublicPort":55555,"Type":"tcp"}]

data PortMap = PortMap
            { _ip          :: IP
            , _privatePort :: Port
            , _publicPort  :: Port
            , _type        :: PortType
            } deriving (Show, Eq)


data DockerContainer = DockerContainer
                    { _containerId        :: ResourceId
                    , _containerImageId   :: ResourceId
                    , _command            :: String
                    , _containerCreatedAt :: Int
                    , _names              :: [String]
                    , _status             :: String
                    , _ports              :: Maybe [PortMap]
                    } deriving (Show, Eq)


data CreateContainerOpts = CreateContainerOpts
                  { _hostname       :: String
                  , _user           :: String
                  , _memory         :: Int
                  , _memorySwap     :: Int
                  , _attachStdin    :: Bool
                  , _attachStdout   :: Bool
                  , _attachStderr   :: Bool
                  , _portSpecs      :: Maybe Object
                  , _tty            :: Bool
                  , _openStdin      :: Bool
                  , _stdinOnce      :: Bool
                  , _env            :: Maybe Object
                  , _cmd            :: [String]
                  , _image          :: String
                  , _volumes        :: Maybe Object
                  , _volumesFrom    :: Maybe Object
                  , _workingDir     :: String
                  , _disableNetwork :: Bool
                  , _exposedPorts   :: Maybe Object
                  } deriving (Show)

defaultCreateOpts = CreateContainerOpts {
                             _hostname = ""
                            , _user = ""
                            , _memory = 0
                            , _memorySwap =  0
                            , _attachStdin = False
                            , _attachStdout = False
                            , _attachStderr = False
                            , _portSpecs = Nothing
                            , _tty = False
                            , _openStdin =  False
                            , _stdinOnce = False
                            , _env = Nothing
                            , _cmd = []
                            , _image = "debian"
                            , _volumes = Nothing
                            , _volumesFrom =  Nothing
                            , _workingDir = ""
                            , _disableNetwork = False
                            , _exposedPorts = Nothing
                            }

instance ToJSON CreateContainerOpts where
        toJSON (CreateContainerOpts {..}) = object
            [ "Hostname" .= _hostname
            , "User" .= _user
            , "Memory" .= _memory
            , "MemorySwap" .= _memorySwap
            , "AttachStdin" .= _attachStdin
            , "AttachStdout" .= _attachStdout
            , "AttachStderr" .= _attachStderr
            , "PortSpecs" .= _portSpecs
            , "Tty" .= _tty
            , "OpenStdin" .= _openStdin
            , "StdinOnce" .= _stdinOnce
            , "Env" .= _env
            , "Cmd" .= _cmd
            , "Image" .= _image
            , "Volumes" .= _volumes
            , "VolumesFrom" .= _volumesFrom
            , "WrokingDir" .= _workingDir
            , "DisableNetwork" .= _disableNetwork
            , "ExposedPorts" .= _exposedPorts
            ]

-- data CreateContainerResponse = CreateContainerResponse
--                               { _createdContainerId :: String
--                               , _warnings           :: Maybe [T.Text]
--                               } deriving (Show)

data StartContainerOpts = StartContainerOpts
                        { _Binds           :: [T.Text]
                        , _Links           :: [T.Text]
                        , _LxcConf         :: [(T.Text, T.Text)]
                        , _PortBindings    :: [(T.Text, [(T.Text, T.Text)])]
                        , _PublishAllPorts :: Bool
                        , _Privileged      :: Bool
                        , _Dns             :: [T.Text]
                        , _VolumesFrom     :: [T.Text]
                        } deriving (Show)

defaultStartOpts = StartContainerOpts
                { _Binds = []
                , _Links = []
                , _LxcConf = []
                , _PortBindings = []
                , _PublishAllPorts = False
                , _Privileged = False
                , _Dns = []
                , _VolumesFrom = []
                }

instance ToJSON StartContainerOpts where
        toJSON (StartContainerOpts {..}) = object
            [ "Binds" .= _Binds
            , "Links" .= _Links
            , "LxcConf" .= _LxcConf
            , "PortBindings" .= _PortBindings
            , "PublishAllPorts" .= _PublishAllPorts
            , "Privileged" .= _Privileged
            , "Dns" .= _Dns
            , "VolumesFrom" .= _VolumesFrom
            ]

makeClassy ''ResourceId

-- makeLenses ''CreateContainerResponse
makeLenses ''DockerImage
makeLenses ''DockerContainer
makeLenses ''CreateContainerOpts

instance HasResourceId DockerImage where
        resourceId = imageId

instance FromJSON DockerImage where
        parseJSON (Object v) =
            DockerImage <$> ResourceId <$> (v .: "Id")
                <*> (v .: "Created")
                <*> (v .:? "ParentId")
                <*> (v .: "RepoTags")
                <*> (v .: "Size")
                <*> (v .: "VirtualSize")

instance FromJSON PortMap where
        parseJSON (Object v) =
            PortMap <$> (v .: "IP")
                <*> (v .: "PrivatePort")
                <*> (v .: "PublicPort")
                <*> (v .: "Type")

instance HasResourceId DockerContainer where
        resourceId = containerId

instance FromJSON DockerContainer where
        parseJSON (Object v) =
            DockerContainer <$> (ResourceId <$> (v .: "Id"))
                <*> (ResourceId <$> (v .: "Id"))
                <*> (v .: "Command")
                <*> (v .: "Created")
                <*> (v .: "Names")
                <*> (v .: "Status")
                <*> (v .:? "Ports")

-- instance FromJSON CreateContainerResponse where
--         parseJSON (Object v) =
--             CreateContainerResponse <$> (v .: "Id")
--                 <*> (v .:? "warnings")

$(deriveJSON dopts ''DockerVersion)