{-# LANGUAGE ScopedTypeVariables, LambdaCase #-}
module Main where {
import System.Environment(getArgs);
import Control.Exception(assert);
import Debug.Trace(trace);
import Data.Function((&));
import Control.Category((>>>));
import Prelude hiding((.),(>>));
-- sadly this is going to move
import Distribution.Version(VersionRange,unionVersionRanges,intersectVersionRanges,earlierVersion,laterVersion,withinVersion, thisVersion, orEarlierVersion, orLaterVersion);
import qualified Distribution.Version as Version;
--import System.IO(hSetBuffering,stdout,BufferMode(LineBuffering));
--import System.IO(stderr,hPutStrLn);
import qualified Data.List as List;
--import qualified Control.Monad as Monad;
--import qualified Data.Maybe as Maybe;
import Data.Maybe(catMaybes);
import qualified Data.Map as Map; import Data.Map(Map);
--import qualified Data.Set as Set; import Data.Set(Set);
import qualified Data.Bifunctor as Bifunctor;
-- NB: As of Cabal 2, Distribution.Version is distinct from Data.Version.
import Distribution.Types.Version(mkVersion,Version);
import Distribution.Pretty(prettyShow);

type I = Integer;

-- to avoid the redundancy warning
trace_placeholder :: ();
trace_placeholder = (trace,assert) & (id >>> error) "trace_placeholder";

main :: IO();
main = getArgs >>= \case{
["go"] -> print testv;
["pl"] -> getContents >>= (lines >>> mapM_ (read >>> toversionrange >>> print));
["pt"] -> getContents >>= (gentuples >>> mapM_ print);
["makemap"] -> getContents >>= (gentuples >>> Map.fromList >>> print);
["latests",fn] -> dotest fn;
_ -> undefined;
};

testv :: VersionRange;
testv = foldr1 unionVersionRanges [laterVersion $ mkVersion [1,2,3,4]];

type Versionnumber = [Int];
-- this is easier to Read
data Myversion = And Myversion Myversion | Singleton Operator Versionnumber | Within Versionnumber deriving(Read,Show);

data Operator = Vgt | Vlt | Veq | Vge | Vle deriving (Read,Show);

toversionrange :: Myversion -> VersionRange;
toversionrange (And x y) = intersectVersionRanges (toversionrange x) (toversionrange y);
toversionrange (Singleton op num) = (tofunc op) (mkVersion num);
toversionrange (Within num) = withinVersion (mkVersion num);

tofunc :: Operator -> Version -> VersionRange;
tofunc Vgt = laterVersion;
tofunc Vlt = earlierVersion;
tofunc Veq = thisVersion;
tofunc Vle = orEarlierVersion;
tofunc Vge = orLaterVersion;

readtuple :: String -> (String,[Myversion]);
readtuple = read;

gentuples :: String -> [(String,VersionRange)];
gentuples = lines >>> map (readtuple >>> Bifunctor.second (map toversionrange >>> foldr1 unionVersionRanges));

dotest :: String -> IO();
dotest mapfn = do {
vmap <- readmap mapfn;
-- print vmap;
getContents >>= (lines >>> map (readvline >>> (getlatest vmap)) >>> catMaybes >>> map outpackage >>> mapM_ putStrLn);
};

type Vmap = Map String VersionRange;

readmap :: String -> IO Vmap;
readmap fn = readFile fn >>= (read >>> return);

sortednub :: (Eq a) => [a] -> [a];
sortednub = List.group >>> map head;

-- presumably output from perl
readvline :: String -> (String,[Version]);
readvline = read >>> Bifunctor.second (map mkVersion >>> List.sort >>> sortednub >>> reverse);

getlatest :: Vmap -> (String,[Version]) -> Maybe (String,Version);
getlatest vm (p,vs) = let {
 bad :: Version -> Bool;
 bad v = case goodrange of {
Nothing -> False; -- no preferences mean every version is good
Just good -> Version.withinRange v good & not;
};
 goodrange :: Maybe VersionRange;
 goodrange = Map.lookup p vm;
} in do {
v <- dropWhile bad vs & maybehead;
return (p, v)
};

outpackage :: (String,Version) -> String;
outpackage (p,v) = p++"-"++prettyShow v;

-- some packages are disabled by having every version not preferred or deprecated
maybehead :: [a] -> Maybe a;
maybehead [] = Nothing;
maybehead (x:_) = Just x;
} --end
