--{-# LANGUAGE TupleSections #-}
module Dodge.Combine 
    ( combinePoss
    , combineSizes
    , combineItemListYou
    , combineListInfo
    , toggleCombineInv
    , enterCombineInv
    ) where
import Dodge.Combine.Combinations
import Dodge.Base.You
import Dodge.Data
import Dodge.Item.Amount
import Dodge.Inventory.ItemSpace
--import Dodge.Combine.Module
import Dodge.Module
--import Dodge.Combine.Data
--import Multiset
import SimpleTrie

import Data.Map.Merge.Strict
import Control.Monad
import Control.Lens
import Data.Bifunctor
import qualified Data.IntMap.Strict as IM
--import qualified Data.IntSet as IS
import qualified Data.Map.Strict as M
--import Data.Map.Merge.Strict
import Data.Maybe
import Data.List (scanl',sortOn,sort)

combinationsTrie :: Trie (IcAmount,ItemBaseType) Item
combinationsTrie = foldr 
    (uncurry insertInTrie . first sort) 
    emptyTrie
    itemCombinations

invertInventory :: IM.IntMap Item -> [(ItemBaseType,IcAmount,Int)]
invertInventory = IM.foldrWithKey
    (\k it -> ((_iyBase $ _itType it, itStackAmount it,k) :) )
    []

splitIcAmounts :: [(ItemBaseType,IcAmount,Int)] -> [((IcAmount,ItemBaseType),(IcAmount,Int))]
splitIcAmounts = concatMap f
  where
    f (x,n,y) = [((i,x),(i,y)) | i <- [1..n]]

lookupItems :: IM.IntMap Item -> [([(IcAmount,Int)],Item)]
lookupItems = flip multiLookupTrieI combinationsTrie . sortOn fst . splitIcAmounts . invertInventory

combineItemListYouX :: World -> [([Int],Item)]
combineItemListYouX = map (first $ concatMap g) . lookupItems . yourInv
  where
    g (amount,i) = replicate (_toInt amount) i

combineListInfo :: World -> [([Int],([String],Item))]
combineListInfo w = filter (f . snd . snd) . map (cmm inv) $ combineItemListYouX w
  where
    inv = yourInv w
    f itm = _itType itm `notElem` fmap _itType inv

combineItemListYou :: World -> [([Int],Item)]
combineItemListYou = map (second snd) . combineListInfo

cmm :: IM.IntMap Item -> ([Int],Item) -> ([Int],([String],Item))
cmm inv (is,itm) = (is,itm & itType . iyModules %%~ flip combineModuleMaps mms)
  where
    mms = map (_iyModules . _itType . (inv IM.!)) is
    
combineModuleMaps :: M.Map ModuleSlot ItemModuleType 
    -> [M.Map ModuleSlot ItemModuleType]
    -> ([String],M.Map ModuleSlot ItemModuleType)
combineModuleMaps = foldM combineTwoModuleMaps

combineTwoModuleMaps :: M.Map ModuleSlot ItemModuleType
    -> M.Map ModuleSlot ItemModuleType
    -> ([String],M.Map ModuleSlot ItemModuleType)
combineTwoModuleMaps = mergeA 
    preserveMissing
    (filterAMissing f)
    (zipWithAMatched g)
  where
    f _ EMPTYMODULE = ([],False)
    f _ md = ([rm "REMOVES" md],False)
    g _ EMPTYMODULE md = ([],md)
    g _ md EMPTYMODULE = ([],md)
    g _ md2 md1 = ([rm "REPLACES" md1 ++ rm " WITH" md2],md2)
    rm str md = str ++ " "++fullModuleName md
-- g above could be a monoid of some description...

fullModuleName :: ItemModuleType -> String
fullModuleName = fromMaybe "EMPTYMODULE" . moduleName

toggleCombineInv :: World -> World
toggleCombineInv w = case _hudElement (_hud w) of
    DisplayInventory CombineInventory {} -> w & hud . hudElement .~ DisplayInventory NoSubInventory
    _ -> w & enterCombineInv

enterCombineInv :: World -> World
enterCombineInv w = w & hud . hudElement .~ DisplayInventory (CombineInventory mi)
  where
    mi = 0 <$ listToMaybe (combineItemListYou w)

combineSizes :: World -> [Int]
combineSizes = map (itSlotsTaken . snd) . combineItemListYou

combinePoss :: World -> [Int]
combinePoss = scanl' (+) 0 . combineSizes
