module Penny.Lincoln.Bits.Price (
    From ( From, unFrom )
  , To ( To, unTo )
  , CountPerUnit ( CountPerUnit, unCountPerUnit )
  , Price ( from, to, countPerUnit )
  , newPrice
  ) where

import Data.Monoid (mconcat)
import qualified Penny.Lincoln.Equivalent as Ev
import Penny.Lincoln.Equivalent ((==~))
import qualified Penny.Lincoln.Bits.Open as O
import Penny.Lincoln.Bits.Qty (QtyRep)


newtype From = From { unFrom :: O.Commodity }
  deriving (Eq, Ord, Show)

newtype To = To { unTo :: O.Commodity }
  deriving (Eq, Ord, Show)

newtype CountPerUnit = CountPerUnit { unCountPerUnit :: QtyRep }
  deriving (Eq, Ord, Show)

instance Ev.Equivalent CountPerUnit where
  equivalent (CountPerUnit x) (CountPerUnit y) = x ==~ y
  compareEv (CountPerUnit x) (CountPerUnit y) = Ev.compareEv x y

data Price = Price { from :: From
                   , to :: To
                   , countPerUnit :: CountPerUnit }
             deriving (Eq, Ord, Show)

-- | Two Price are equivalent if the From and To are equal and the
-- CountPerUnit is equivalent.

instance Ev.Equivalent Price where
  equivalent (Price xf xt xc) (Price yf yt yc) =
    xf == yf && xt == yt && xc ==~ yc

  compareEv (Price xf xt xc) (Price yf yt yc) = mconcat
    [ compare xf yf
    , compare xt yt
    , Ev.compareEv xc yc
    ]

-- | Succeeds only if From and To are different commodities.
newPrice :: From -> To -> CountPerUnit -> Maybe Price
newPrice f t cpu =
  if unFrom f == unTo t
  then Nothing
  else Just $ Price f t cpu