{-# LANGUAGE DeriveDataTypeable, TemplateHaskell #-}
module Elm.Internal.Version where

import           Control.Applicative
import           Data.Aeson
import           Data.Binary
import           Data.Char              (isDigit)
import qualified Data.List              as List
import           Data.Typeable
import qualified Data.Text              as T
import qualified Paths_Elm as This
import qualified Data.Version as Version

elmVersion :: Version
elmVersion = V ns ""
    where (Version.Version ns _) = This.version

-- Data representation

data Version = V [Int] String
    deriving (Typeable,Eq)

instance Ord Version where
  compare (V ns tag) (V ns' tag') =
      case compare ns ns' of
        EQ -> compare tag' tag -- reverse comparison to favor ""
        cmp -> cmp

instance Show Version where
  show (V ns tag) =
      List.intercalate "." (map show ns) ++ if null tag then "" else "-" ++ tag

instance Binary Version where
  get = V <$> get <*> get
  put (V ns tag) = do put ns
                      put tag

tagless :: Version -> Bool
tagless (V _ tag) = null tag

fromString :: String -> Maybe Version
fromString version = V <$> splitNumbers possibleNumbers <*> tag
    where
      (possibleNumbers, possibleTag) = break (=='-') version

      tag = case possibleTag of
              "" -> Just ""
              '-':rest -> Just rest
              _ -> Nothing

      splitNumbers :: String -> Maybe [Int]
      splitNumbers ns =
          case span isDigit ns of
            ("", _) -> Nothing
            (number, []) -> Just [read number]
            (number, '.':rest) -> (read number :) <$> splitNumbers rest
            _ -> Nothing

instance FromJSON Version where
    parseJSON (String text) =
        let string = T.unpack text in
        case fromString string of
          Just v -> return v
          Nothing -> fail $ unlines
                     [ "Dependency file has an invalid version number: " ++ string
                     , "Must have format 0.1.2 or 0.1.2-tag"
                     ]

    parseJSON _ = fail "Version number must be stored as a string."

instance ToJSON Version where
    toJSON version = toJSON (show version)