module Graphics.Rendering.Chart.Axis.Int(
    defaultIntAxis,
    scaledIntAxis,
    autoScaledIntAxis
) where
import Data.List(genericLength)
import Graphics.Rendering.Chart.Geometry
import Graphics.Rendering.Chart.Axis.Types
import Graphics.Rendering.Chart.Axis.Floating
instance PlotValue Int where
    toValue    = fromIntegral
    fromValue  = round
    autoAxis   = autoScaledIntAxis defaultIntAxis
instance PlotValue Integer where
    toValue    = fromIntegral
    fromValue  = round
    autoAxis   = autoScaledIntAxis defaultIntAxis
defaultIntAxis :: (Show a) => LinearAxisParams a
defaultIntAxis  = LinearAxisParams {
    _la_labelf  = map show,
    _la_nLabels = 5,
    _la_nTicks  = 10
}
autoScaledIntAxis :: (Integral i, PlotValue i) =>
                     LinearAxisParams i -> AxisFn i
autoScaledIntAxis lap ps = scaledIntAxis lap rs ps
  where
    rs = (minimum ps,maximum ps)
scaledIntAxis :: (Integral i, PlotValue i) =>
                 LinearAxisParams i -> (i,i) -> AxisFn i
scaledIntAxis lap (minI,maxI) ps =
    makeAxis (_la_labelf lap) (labelvs,tickvs,gridvs)
  where
    range []  = (0,1)
    range _   | minI == maxI = (fromIntegral $ minI1, fromIntegral $ minI+1)
              | otherwise    = (fromIntegral   minI,   fromIntegral   maxI)
    labelvs   = stepsInt (fromIntegral $ _la_nLabels lap) r
    tickvs    = stepsInt (fromIntegral $ _la_nTicks lap)
                                  ( fromIntegral $ minimum labelvs
                                  , fromIntegral $ maximum labelvs )
    gridvs    = labelvs
    r         = range ps
stepsInt :: Integral a => a -> Range -> [a]
stepsInt nSteps range = bestSize (goodness alt0) alt0 alts
  where
    bestSize n a (a':as) = let n' = goodness a' in
                           if n' < n then bestSize n' a' as else a
    bestSize _ _ []      = []
    goodness vs          = abs (genericLength vs  nSteps)
    (alt0:alts)          = map (\n -> steps n range) sampleSteps'
    
    
    sampleSteps'         = let rangeMag = ceiling (snd range  fst range)
                               
                               (s1,s2) = span (< (rangeMag `div` nSteps)) sampleSteps
                           in ((reverse . take 5 . reverse) s1) ++ s2
    
    sampleSteps          = [1,2,5] ++ sampleSteps1
    sampleSteps1         = [10,20,25,50] ++ map (*10) sampleSteps1
    steps size (minV,maxV) = takeWhile (<b) [a,a+size..] ++ [b]
      where
        a = (floor   (minV / fromIntegral size)) * size
        b = (ceiling (maxV / fromIntegral size)) * size