{-# LANGUAGE DeriveDataTypeable #-}

module Propellor.Types.OS (
        System(..),
        Distribution(..),
        TargetOS(..),
        DebianKernel(..),
        DebianSuite(..),
        FreeBSDRelease(..),
        FBSDVersion(..),
        isStable,
        Release,
        Architecture(..),
        architectureToDebianArchString,
        HostName,
        UserName,
        User(..),
        Group(..),
        userGroup,
        Port(..),
        systemToTargetOS,
) where

import Propellor.Types.ConfigurableValue

import Network.Socket (HostName)
import Data.Typeable
import Data.String

-- | High level description of a operating system.
data System = System Distribution Architecture
        deriving (Show, Eq, Typeable)

data Distribution
        = Debian DebianKernel DebianSuite
        | Buntish Release -- ^ A well-known Debian derivative founded by a space tourist. The actual name of this distribution is not used in Propellor per <http://joeyh.name/blog/entry/trademark_nonsense/>
        | ArchLinux
        | FreeBSD FreeBSDRelease
        deriving (Show, Eq)

-- | Properties can target one or more OS's; the targets are part
-- of the type of the property, so need to be kept fairly simple.
data TargetOS
        = OSDebian
        | OSBuntish
        | OSArchLinux
        | OSFreeBSD
        deriving (Show, Eq, Ord)

systemToTargetOS :: System -> TargetOS
systemToTargetOS (System (Debian _ _) _) = OSDebian
systemToTargetOS (System (Buntish _) _) = OSBuntish
systemToTargetOS (System (ArchLinux) _) = OSArchLinux
systemToTargetOS (System (FreeBSD _) _) = OSFreeBSD

-- | Most of Debian ports are based on Linux. There also exist hurd-i386,
-- kfreebsd-i386, kfreebsd-amd64 ports
data DebianKernel = Linux | KFreeBSD | Hurd
        deriving (Show, Eq)

-- | Debian has several rolling suites, and a number of stable releases,
-- such as Stable "buster".
data DebianSuite = Experimental | Unstable | Testing | Stable Release
        deriving (Show, Eq)

-- | FreeBSD breaks their releases into "Production" and "Legacy".
data FreeBSDRelease = FBSDProduction FBSDVersion | FBSDLegacy FBSDVersion
        deriving (Show, Eq)

data FBSDVersion = FBSD101 | FBSD102 | FBSD093
        deriving (Eq)

instance IsString FBSDVersion where
        fromString "10.1-RELEASE" = FBSD101
        fromString "10.2-RELEASE" = FBSD102
        fromString "9.3-RELEASE" = FBSD093
        fromString _ = error "Invalid FreeBSD release"

instance ConfigurableValue FBSDVersion where
        val FBSD101 = "10.1-RELEASE"
        val FBSD102 = "10.2-RELEASE"
        val FBSD093 = "9.3-RELEASE"

instance Show FBSDVersion where
        show = val

isStable :: DebianSuite -> Bool
isStable (Stable _) = True
isStable _ = False

type Release = String

-- | Many of these architecture names are based on the names used by
-- Debian, with a few exceptions for clarity.
data Architecture
        = X86_64 -- ^ 64 bit Intel, called "amd64" in Debian
        | X86_32 -- ^ 32 bit Intel, called "i386" in Debian
        | ARMHF
        | ARMEL
        | PPC
        | PPC64
        | SPARC
        | SPARC64
        | MIPS
        | MIPSEL
        | MIPS64EL
        | SH4
        | IA64 -- ^ Itanium
        | S390
        | S390X
        | ALPHA
        | HPPA
        | M68K
        | ARM64
        | X32 -- ^ New Linux ABI for 64 bit CPUs using 32-bit integers. Not widely used.
        deriving (Show, Eq)

architectureToDebianArchString :: Architecture -> String
architectureToDebianArchString X86_64 = "amd64"
architectureToDebianArchString X86_32 = "i386"
architectureToDebianArchString ARMHF = "armhf"
architectureToDebianArchString ARMEL = "armel"
architectureToDebianArchString PPC = "powerpc"
architectureToDebianArchString PPC64 = "ppc64el"
architectureToDebianArchString SPARC = "sparc"
architectureToDebianArchString SPARC64 = "sparc64"
architectureToDebianArchString MIPS = "mips"
architectureToDebianArchString MIPSEL = "mipsel"
architectureToDebianArchString MIPS64EL = "mips64el"
architectureToDebianArchString SH4 = "sh"
architectureToDebianArchString IA64 = "ia64"
architectureToDebianArchString S390 = "s390"
architectureToDebianArchString S390X = "s390x"
architectureToDebianArchString ALPHA = "alpha"
architectureToDebianArchString HPPA = "hppa"
architectureToDebianArchString M68K = "m68k"
architectureToDebianArchString ARM64 = "arm64"
architectureToDebianArchString X32 = "x32"

type UserName = String

newtype User = User UserName
        deriving (Eq, Ord, Show)

instance ConfigurableValue User where
        val (User n) = n

newtype Group = Group String
        deriving (Eq, Ord, Show)

instance ConfigurableValue Group where
        val (Group n) = n

-- | Makes a Group with the same name as the User.
userGroup :: User -> Group
userGroup (User u) = Group u

newtype Port = Port Int
        deriving (Eq, Ord, Show)

instance ConfigurableValue Port where
        val (Port p) = show p