module Components.DataProcessors.ListDataProcessor (processReturnedValues,processAggReturnedValues) where

import Data.Text (Text,unpack,pack)
import Data.List (unzip4)
import Text.JSON (
    showJSON,
    showJSONs,
    JSValue,
    JSObject,
    toJSObject,
    encodeStrict,
    JSValue(JSNull),
    JSON(..),
    Result(Ok),
    decode
  )
import Control.Exception (throw)
import Data.Foldable (foldl',foldr')
import Data.Int (Int64)
import Model.ServerExceptions (
    VariableException(InvalidVariableTypeException),
    QueryException(ReadJsonException)
  )
import Model.ServerObjectTypes (
    NestedObject(..),
    Field,
    RootObject,
    ServerObject,
    ScalarType(..),
    Transformation,
    Argument,
    InlinefragmentObject(..)
  )
import Components.ObjectHandlers.ObjectsHandler (
    isServerObjectTable,
    getNestedObjectFieldLabel,
    getScalarFieldLabel,
    translateTableToObject,
    countTableIds
  )
import Components.Util (fst3,snd3,thd3,fst4,thd4,fth4)

-- root objects to one json representation of separate graphql results
processReturnedValues :: (Eq a) => ((a -> Text),(a -> Double),(a -> Int64),(a -> Bool),(a -> Bool)) -> [(String,[(String,String,[(String,[(String,String,String,String)])])])] -> [(String,[String],String)] -> [(String,[String],[String])] -> [RootObject] -> [[[(Int,Bool,String)]]] -> [[[[[a]]]]] -> String
processReturnedValues transFx sss sodn soa robjs tbls rlts = encodeStrict $ processReturnedValuesToJsonObject transFx sss sodn soa robjs tbls rlts
processReturnedValuesToJsonObject :: (Eq a) => ((a -> Text),(a -> Double),(a -> Int64),(a -> Bool),(a -> Bool)) -> [(String,[(String,String,[(String,[(String,String,String,String)])])])] -> [(String,[String],String)] -> [(String,[String],[String])] -> [RootObject] -> [[[(Int,Bool,String)]]] -> [[[[[a]]]]] -> JSObject (JSObject JSValue)
processReturnedValuesToJsonObject transFx sss sodn soa robjs tbls rlts = toJSObject [("data", toJSObject [processReturnedValueByRootObject transFx sss sodn soa a b c | (a,b,c) <- zip3 robjs tbls rlts])]
-- qraphql query object and sql return data to json representation on graphql query results
processReturnedValueByRootObject :: (Eq a) => ((a -> Text),(a -> Double),(a -> Int64),(a -> Bool),(a -> Bool)) -> [(String,[(String,String,[(String,[(String,String,String,String)])])])] -> [(String,[String],String)] -> [(String,[String],[String])] -> RootObject -> [[(Int,Bool,String)]] -> [[[[a]]]] -> (String, JSValue)
processReturnedValueByRootObject transFx sss sodn soa (NestedObject Nothing name sobj _ sfs) tbls rlts = (name, showJSONs $ packageSubFields transFx sss sodn soa sobj tbls sfs rlts)
processReturnedValueByRootObject transFx sss sodn soa (NestedObject (Just alias) name sobj _ sfs) tbls rlts = (alias, showJSONs $ packageSubFields transFx sss sodn soa sobj tbls sfs rlts)
-- SubFields and data rows to json representation on qraphql query data
packageSubFields :: (Eq a) => ((a -> Text),(a -> Double),(a -> Int64),(a -> Bool),(a -> Bool)) -> [(String,[(String,String,[(String,[(String,String,String,String)])])])] -> [(String,[String],String)] -> [(String,[String],[String])] -> String -> [[(Int,Bool,String)]] -> [Field] -> [[[[a]]]] -> [JSValue]
packageSubFields transFx sss sodn soa sobj tbls sfs rlts = packageSubFieldsForEveryRow transFx sss sodn soa sobj sfs grpdInstcByObjInst
  where
    dtaByInstCbntn = zip tbls rlts
    grpdInstcByObjInst = foldl' (\rlt (nInst,nDta)->if (snd3 $ head nInst)==False then
          ((init rlt)++[(last rlt)++[(nInst,(nDta,snd $ snd $ head $ last rlt))]])
        else
          rlt++[[(nInst,(nDta,countTableIds (thd3 $ head nInst) sodn))]]) [[(fst $ head dtaByInstCbntn,(snd $ head dtaByInstCbntn,countTableIds (thd3 $ head $ fst $ head dtaByInstCbntn) sodn))]] $ tail dtaByInstCbntn
packageSubFieldsForEveryRow :: (Eq a) => ((a -> Text),(a -> Double),(a -> Int64),(a -> Bool),(a -> Bool)) -> [(String,[(String,String,[(String,[(String,String,String,String)])])])] -> [(String,[String],String)] -> [(String,[String],[String])] -> String -> [Field] -> [[([(Int,Bool,String)],([[[a]]],Int))]] -> [JSValue]
packageSubFieldsForEveryRow transFx sss sodn soa sobj (sf:sfs) dt = if null objWInst then [] else (++) objects $ packageSubFieldsForEveryRow transFx sss sodn soa sobj (sf:sfs) remIs
  where
    (_,_,_,_,isNull) = transFx
    objInstWRw = map (filter (\(_,(nDt,_))->(null $ head nDt)==False)) dt
    objWInst = filter (not . null) objInstWRw
    (nxtObjsRw,remIs) = foldl' (\(objRows,remRows) nObj->
      let (nObjRow,nObjRem) = foldl' (\(nInstRow,nInstRem) (tbls,(dSet,idC))->let (rowData,remData) = separateGqlRow isNull idC dSet in (nInstRow++[(tbls,rowData)],nInstRem++[(tbls,(remData,idC))])) ([],[]) nObj
      in (objRows++[unzip nObjRow],remRows++[nObjRem])) ([],[]) objWInst
    objects = map (\(nTbl,nDt)->showJSON $ toJSObject $ makeOneGQLObject transFx sss sodn soa sobj nTbl (sf:sfs) nDt) nxtObjsRw
packageSubFieldsForEveryRow transFx sss sodn soa sobj [] dt = []

makeOneGQLObject :: (Eq a) => ((a -> Text),(a -> Double),(a -> Int64),(a -> Bool),(a -> Bool)) -> [(String,[(String,String,[(String,[(String,String,String,String)])])])] -> [(String,[String],String)] -> [(String,[String],[String])] -> String -> [[(Int,Bool,String)]] -> [Field] -> [[[[a]]]] -> [(String,JSValue)]
makeOneGQLObject _ _ _ _ _ [] _ _ = error "EOF data processing (source error)"  -- no reference tables (unusual)/all removed
makeOneGQLObject _ _ _ _ _ ([]:_) _ _ = error "EOF data processing (source error)"  -- no reference tables in first varient (unusual)
makeOneGQLObject transFx sss sodn soa sobj tbls ((Left (ScalarType alias "__typename" trans arg)):b) dat = (getScalarFieldLabel $ ScalarType alias "__typename" trans arg, showJSON $ pack $ translateTableToObject (thd3 $ head $ head tbls) sodn):(makeOneGQLObject transFx sss sodn soa sobj tbls b dat)
makeOneGQLObject (fx1,fx2,fx3,fx4,isNull) sss sodn soa sobj tbls ((Left (ScalarType alias name trans arg)):b) ((((i:j):k):l):m) = (getScalarFieldLabel $ ScalarType alias name trans arg, if isNull i then JSNull else castJSType (fx1,fx2,fx3,fx4,isNull) (findPrimitiveScalarTypeType (translateTableToObject (thd3 $ head $ head tbls) sodn) name trans arg sss) i):(makeOneGQLObject (fx1,fx2,fx3,fx4,isNull) sss sodn soa sobj tbls b (((j:k):l):m))
makeOneGQLObject transFx sss sodn soa sobj tbls ((Right (Left (NestedObject alias name nso ss sfs))):b) ((j:k):l) = ((getNestedObjectFieldLabel $ NestedObject alias name nso ss sfs), showJSONs (packageSubFields transFx sss sodn soa nso nxtTbls sfs nxtData)):(makeOneGQLObject transFx sss sodn soa sobj remTbls b remData)
  where
    (nxtTbls,nxtData,remTbls,remData) = separateDataFrNstdObj tbls ((j:k):l)
makeOneGQLObject transFx sss sodn soa sobj tbls ((Right (Right (InlinefragmentObject ifo sfs))):b) dat = if (isServerObjectTable (thd3 $ head $ head tbls) ifo sodn soa) then makeOneGQLObject transFx sss sodn soa sobj tbls (sfs++b) dat else makeOneGQLObject transFx sss sodn soa sobj tbls b dat
makeOneGQLObject _ _ _ _ _ _ [] ((([]:_):_):_) = []  -- no columns (in first variant and first query) and no fields (done)
makeOneGQLObject _ _ _ _ _ _ _ (([]:_):_) = []  -- no rows (no data)
makeOneGQLObject _ _ _ _ _ _ ((Right (Left _)):[]) ([]:_) = error "EOF data processing (source error)"  -- field and no queries (unusual)
makeOneGQLObject _ _ _ _ _ _ _ ([]:_) = []  -- no queries (no data)
makeOneGQLObject _ _ _ _ _ _ _ [] = []  -- no instance combination (unusual)
makeOneGQLObject _ _ _ _ _ _ [] _ = [] -- columns and no fields (done or error)
makeOneGQLObject _ _ _ _ _ _ ((Left _):_) ((([]:_):_):_) = error "EOF data processing (source error)"  -- field and no result columns

separateDataFrNstdObj :: [[(Int,Bool,String)]] -> [[[[a]]]] -> ([[(Int,Bool,String)]],[[[[a]]]],[[(Int,Bool,String)]],[[[[a]]]])
separateDataFrNstdObj info dat =
    foldl' (\(nTbls,nDt,rTbls,rDt) (instMt,instDt)->
      let
        nxtTblsInfo = tail instMt
        tNxtTblsInfo = tail nxtTblsInfo
        (objLvl,_,_) = head nxtTblsInfo
        nestedObjCount = foldr' (\(nlvl,_,_) rlt->if nlvl<=objLvl then 0 else rlt+1) 0 tNxtTblsInfo
        fQrydPos = foldr' (\(idx,(_,fst,_)) rlt->if fst then idx else rlt) ((length nxtTblsInfo)-1) $ zip [0..] nxtTblsInfo
        hasQrd = (<=) fQrydPos nestedObjCount
        (nObjMeta,nObjData,remMeta,remData) = if hasQrd then
            ([tail instMt],[instDt],[],[])
          else
            ([],[],[(head instMt):(drop (nestedObjCount+1) $ tail instMt)],[instDt])
      in
        (nTbls++nObjMeta,nDt++nObjData,rTbls++remMeta,rDt++remData))
    ([fstNObjMeta],[fstNObjData],[fstRemMeta],[fstRemData]) $
    tail $ zip info dat
  where
    fstInstInfo = head info
    fstInstDat = head dat
    (fstL,fstF,fstT) = head fstInstInfo
    nObjPlusTblsInfo = tail fstInstInfo
    nObjLvl = (+) fstL 1
    nstdObjsCount = foldr' (\(nlvl,_,_) rlt->if (==) nlvl nObjLvl then 1 else (+) rlt 1) 1 $ tail nObjPlusTblsInfo
    fstNObjMeta = take nstdObjsCount nObjPlusTblsInfo
    nObjPlusDat = tail $ fstInstDat
    fstNObjData = take nstdObjsCount nObjPlusDat
    fstRemMeta = (fstL,fstF,fstT):(drop nstdObjsCount nObjPlusTblsInfo)
    fstRemData = (head $ fstInstDat):(drop nstdObjsCount nObjPlusDat)

findPrimitiveScalarTypeType :: ServerObject -> String -> Transformation -> Argument -> [(String,[(String,String,[(String,[(String,String,String,String)])])])] -> String
findPrimitiveScalarTypeType sobj name trans arg ((obj,flds):rst) = if sobj==obj then findScalarTypeType name trans arg flds else findPrimitiveScalarTypeType sobj name trans arg rst
findPrimitiveScalarTypeType _ _ _ _ [] = error "Object is not found (source error)."
findScalarTypeType :: String -> Transformation -> Argument -> [(String,String,[(String,[(String,String,String,String)])])] -> String
findScalarTypeType name Nothing _ ((sName,sType,opts):rst) = if name==sName then sType else findScalarTypeType name Nothing Nothing rst
findScalarTypeType name (Just trans) arg ((sName,sType,args):rst) = if name==sName then findArgumentOptionType trans arg args else findScalarTypeType name (Just trans) arg rst
findScalarTypeType _ _ _ [] = error "Scalar is not found (source error)."
findArgumentOptionType :: String -> Argument -> [(String,[(String,String,String,String)])] -> String
findArgumentOptionType trans Nothing ((aname,((_,typ,_,_):_)):rst) = if trans==aname then typ else findArgumentOptionType trans Nothing rst
findArgumentOptionType trans Nothing ((aname,[]):rst) = if trans==aname then error "Scalar argument is not found (source error)." else findArgumentOptionType trans Nothing rst
findArgumentOptionType trans (Just arg) ((aname,opts):rst) = if trans==aname then findOptionType arg opts else findArgumentOptionType trans (Just arg) rst
findArgumentOptionType _ _ [] = error "Scalar argument is not found (source error)."
findOptionType :: String -> [(String,String,String,String)] -> String
findOptionType arg ((oname,typ,_,_):rst) = if arg==oname then typ else findOptionType arg rst
findOptionType _ [] = error "Scalar argument option is not found (source error)."

separateGqlRow :: (Eq a) => (a -> Bool) -> Int -> [[[a]]] -> ([[[a]]],[[[a]]])
separateGqlRow isNull idCnt dat = if (all isNull fstTblIds) then
                                    unzip [([],[row | row<-tbl, not $ all isNull $ take idCnt row]) | tbl<-dat]
                                  else
                                    getGqlRow idCnt fstTblIds dat [] []
                                  where
                                    fstTblIds = take idCnt $ head $ head dat
getGqlRow :: (Eq a) => Int -> [a] -> [[[a]]] -> [[[a]]] -> [[[a]]] -> ([[[a]]],[[[a]]])
getGqlRow _ _ [] tblsRows tblsRem = (tblsRows,tblsRem)
getGqlRow idCnt fstIds (tbl:dat) tblsRows tblsRem = getGqlRow idCnt fstIds dat (tblsRows++[nxtRow]) (tblsRem++[nxtRem])
                                                  where
                                                    (nxtRow,nxtRem) = foldl' (\(rowRlt,remRlt) row -> if (==) fstIds $ take idCnt row then
                                                        (rowRlt++[drop idCnt row],remRlt)
                                                      else
                                                        (rowRlt,remRlt++[row])) ([],[]) tbl

castJSType :: ((a -> Text),(a -> Double),(a -> Int64),(a -> Bool),(a -> Bool)) -> String -> a -> JSValue
castJSType (toText,_,_,_,_) "Text" val = showJSON $ toText val
castJSType (_,toNum,_,_,_) "Double" val = showJSON $ toNum val
castJSType (_,_,toInt,_,_) "Int64" val = showJSON $ toInt val
castJSType (_,_,_,toBool,_) "Boolean" val = showJSON $ toBool val
castJSType _ _ _ = throw InvalidVariableTypeException

processAggReturnedValues :: (Eq a) => ((a -> Text),(a -> Double),(a -> Int64),(a -> Bool),(a -> Bool)) -> [(String,[(String,String,[(String,[(String,String,String,String)])])])] -> [(String,[String],String)] -> [(String,[String],[String])] -> [RootObject] -> [[[(Int,Int,Bool,String)]]] -> [[[[[a]]]]] -> String
processAggReturnedValues transFx sss sodn soa robjs tbls rlts = encodeStrict $ processAggReturnedValuesToJsonObject transFx sss sodn soa robjs tbls rlts
processAggReturnedValuesToJsonObject :: (Eq a) => ((a -> Text),(a -> Double),(a -> Int64),(a -> Bool),(a -> Bool)) -> [(String,[(String,String,[(String,[(String,String,String,String)])])])] -> [(String,[String],String)] -> [(String,[String],[String])] -> [RootObject] -> [[[(Int,Int,Bool,String)]]] -> [[[[[a]]]]] -> JSObject (JSObject JSValue)
processAggReturnedValuesToJsonObject transFx sss sodn soa robjs tbls rlts = toJSObject [("data", toJSObject [processAggReturnedValueByRootObject transFx sss sodn soa a b c | (a,b,c) <- zip3 robjs tbls rlts])]
processAggReturnedValueByRootObject :: (Eq a) => ((a -> Text),(a -> Double),(a -> Int64),(a -> Bool),(a -> Bool)) -> [(String,[(String,String,[(String,[(String,String,String,String)])])])] -> [(String,[String],String)] -> [(String,[String],[String])] -> RootObject -> [[(Int,Int,Bool,String)]] -> [[[[a]]]] -> (String, JSValue)
processAggReturnedValueByRootObject transFx sss sodn soa (NestedObject Nothing name sobj _ sfs) tbls rlts = (name, showJSONs $ packageAggSubFields transFx sss sodn soa sobj tbls sfs rlts)
processAggReturnedValueByRootObject transFx sss sodn soa (NestedObject (Just alias) name sobj _ sfs) tbls rlts = (alias, showJSONs $ packageAggSubFields transFx sss sodn soa sobj tbls sfs rlts)
packageAggSubFields :: (Eq a) => ((a -> Text),(a -> Double),(a -> Int64),(a -> Bool),(a -> Bool)) -> [(String,[(String,String,[(String,[(String,String,String,String)])])])] -> [(String,[String],String)] -> [(String,[String],[String])] -> String -> [[(Int,Int,Bool,String)]] -> [Field] -> [[[[a]]]] -> [JSValue]
packageAggSubFields transFx sss sodn soa sobj tbls sfs rlts = packageAggSubFieldsForEveryRow transFx sss sodn soa sobj sfs grpdInstcByObjInst
  where
    dtaByInstCbntn = zip tbls rlts
    grpdInstcByObjInst = foldl' (\rlt (nInst,nDta)->if (thd4 $ head nInst)==False then
          ((init rlt)++[(last rlt)++[(nInst,(nDta,snd $ snd $ head $ last rlt))]])
        else
          rlt++[[(nInst,(nDta,countTableIds (fth4 $ head nInst) sodn))]]) [[(fst $ head dtaByInstCbntn,(snd $ head dtaByInstCbntn,countTableIds (fth4 $ head $ fst $ head dtaByInstCbntn) sodn))]] $ tail dtaByInstCbntn
packageAggSubFieldsForEveryRow :: (Eq a) => ((a -> Text),(a -> Double),(a -> Int64),(a -> Bool),(a -> Bool)) -> [(String,[(String,String,[(String,[(String,String,String,String)])])])] -> [(String,[String],String)] -> [(String,[String],[String])] -> String -> [Field] -> [[([(Int,Int,Bool,String)],([[[a]]],Int))]] -> [JSValue]  -- result is root-instance/table/row/column
packageAggSubFieldsForEveryRow transFx sss sodn soa sobj (sf:sfs) dt = if null objWInst then [] else (++) objects $ packageAggSubFieldsForEveryRow transFx sss sodn soa sobj (sf:sfs) remIs
  where
    (_,_,_,_,isNull) = transFx
    objInstWRw = map (filter (not . null . head . fst . snd)) dt
    objWInst = filter (\nObj->(null nObj)==False) objInstWRw
    (nxtObjsRw,remIs) = foldl' (\(objRows,remRows) nObj->
      let (nObjRow,nObjRem) = foldl' (\(nInstRow,nInstRem) (tbls,(dSet,idC))->let (rowData,remData) = separateGqlRow isNull idC dSet in (nInstRow++[(tbls,rowData)],nInstRem++[(tbls,(remData,idC))])) ([],[]) nObj
      in (objRows++[unzip nObjRow],remRows++[nObjRem])) ([],[]) objWInst

    objects = foldl' (\rlt (nTbl,nDt)->rlt++({-if null nTbl {-no instance-} then [] else -}if (fst4 $ head $ head nTbl)<2 then
            map showJSON $ decodeTextToJSObj $ (\(toTxt,_,_,_,_)->toTxt) transFx $ head $ head $ head $ head $ nDt
          else
            [showJSON $ toJSObject $ makeOneGQLObjFrmAgg transFx sss sodn soa sobj nTbl (sf:sfs) nDt])) [] nxtObjsRw
packageAggSubFieldsForEveryRow transFx sss sodn soa sobj [] dt = []
decodeTextToJSObj :: Text -> [JSObject JSValue]
decodeTextToJSObj dta = case decode $ unpack dta of
  (Ok rlt) -> rlt :: [JSObject JSValue]
  _ -> throw ReadJsonException

makeOneGQLObjFrmAgg :: (Eq a) => ((a -> Text),(a -> Double),(a -> Int64),(a -> Bool),(a -> Bool)) -> [(String,[(String,String,[(String,[(String,String,String,String)])])])] -> [(String,[String],String)] -> [(String,[String],[String])] -> String -> [[(Int,Int,Bool,String)]] -> [Field] -> [[[[a]]]] -> [(String,JSValue)]
makeOneGQLObjFrmAgg _ _ _ _ _ [] _ _ = error "EOF data processing (source error)"  -- no instance combinations
makeOneGQLObjFrmAgg _ _ _ _ _ ([]:_) _ _ = error "EOF data processing (source error)"  -- no reference tables in first instance combination
makeOneGQLObjFrmAgg transFx sss sodn soa sobj tbls ((Left (ScalarType alias "__typename" trans arg)):b) dat = (getScalarFieldLabel $ ScalarType alias "__typename" trans arg, showJSON $ pack $ translateTableToObject (fth4 $ head $ head tbls) sodn):(makeOneGQLObjFrmAgg transFx sss sodn soa sobj tbls b dat)
makeOneGQLObjFrmAgg (fx1,fx2,fx3,fx4,isNull) sss sodn soa sobj tbls ((Left (ScalarType alias name trans arg)):b) ((((i:j):k):l):m) = (getScalarFieldLabel $ ScalarType alias name trans arg, if isNull i then JSNull else castJSType (fx1,fx2,fx3,fx4,isNull) (findPrimitiveScalarTypeType (translateTableToObject (fth4 $ head $ head tbls) sodn) name trans arg sss) i):(makeOneGQLObjFrmAgg (fx1,fx2,fx3,fx4,isNull) sss sodn soa sobj tbls b (((j:k):l):m))

makeOneGQLObjFrmAgg transFx sss sodn soa sobj tbls ((Right (Left (NestedObject alias name nso ss sfs))):b) ((j:k):l) = ((getNestedObjectFieldLabel $ NestedObject alias name nso ss sfs), showJSONs $ packageAggSubFields transFx sss sodn soa nso objTbls sfs objData):(makeOneGQLObjFrmAgg transFx sss sodn soa sobj remTbls b remData)
  where
    (objTbls,objData,remTbls,remData) = sepAggDataFrNstdObj tbls ((j:k):l)
makeOneGQLObjFrmAgg transFx sss sodn soa sobj tbls ((Right (Right (InlinefragmentObject ifo sfs))):b) dat = if isServerObjectTable (fth4 $ head $ head tbls) ifo sodn soa then makeOneGQLObjFrmAgg transFx sss sodn soa sobj tbls (sfs++b) dat else makeOneGQLObjFrmAgg transFx sss sodn soa sobj tbls b dat
makeOneGQLObjFrmAgg _ _ _ _ _ _ [] ((([]:_):_):_) = []  -- no columns (in first variant and first query) and no fields (done)
makeOneGQLObjFrmAgg _ _ _ _ _ _ _ (([]:_):_) = []  -- no rows (no data)
makeOneGQLObjFrmAgg _ _ _ _ _ _ ((Right (Left _)):[]) ([]:_) = error "EOF data processing (source error)"  -- field and no queries (unusual)
makeOneGQLObjFrmAgg _ _ _ _ _ _ _ ([]:_) = []  -- no queries (no data)
makeOneGQLObjFrmAgg _ _ _ _ _ _ _ [] = []  -- no variants (unusual)
makeOneGQLObjFrmAgg _ _ _ _ _ _ [] _ = [] -- columns and no fields (done or error)
makeOneGQLObjFrmAgg _ _ _ _ _ _ ((Left _):_) ((([]:_):_):_) = error "EOF data processing (source error)"  -- field and no result columns

sepAggDataFrNstdObj :: [[(Int,Int,Bool,String)]] -> [[[[a]]]] -> ([[(Int,Int,Bool,String)]],[[[[a]]]],[[(Int,Int,Bool,String)]],[[[[a]]]])
sepAggDataFrNstdObj info dat = ([fstNObjMeta]++(concat nxtRowTbls),[fstNObjData]++(concat nxtRowData),[fstRemMeta]++(concat nxtRemTbls),[fstRemData]++(concat nxtRemData))
  where
    (nxtRowTbls,nxtRowData,nxtRemTbls,nxtRemData) = unzip4 $ map (\(instMt,instDt)->
      let
        nxtTblsInfo = tail instMt
        tNxtTblsInfo = tail nxtTblsInfo
        (_,objLvl,_,_) = head nxtTblsInfo
        nestedObjCount = foldr' (\(_,nlvl,_,_) rlt->if nlvl<=objLvl then 0 else rlt+1) 0 tNxtTblsInfo
        fQrydPos = foldr' (\(idx,(_,_,fst,_)) rlt->if fst then idx else rlt) ((length nxtTblsInfo)-1) $ zip [0..] nxtTblsInfo
        hasQrd = (<=) fQrydPos nestedObjCount
      in
        if hasQrd then
            ([tail instMt],[instDt],[],[])
          else
            ([],[],[(head instMt):(drop (nestedObjCount+1) $ tail instMt)],[instDt])) $ tail $ zip info dat
    fstInstInfo = head info
    fstInstDat = head dat
    (fstC,fstL,fstF,fstT) = head fstInstInfo
    nObjPlusTblsInfo = tail fstInstInfo
    nObjLvl = (+) fstL 1
    nstdObjsCount = foldr' (\(_,nlvl,_,_) rlt->if (==) nlvl nObjLvl then 1 else (+) rlt 1) 1 $ tail nObjPlusTblsInfo
    fstNObjMeta = take nstdObjsCount nObjPlusTblsInfo
    nObjPlusDat = tail $ fstInstDat
    fstNObjData = take nstdObjsCount nObjPlusDat
    fstRemMeta = (fstC,fstL,fstF,fstT):(drop nstdObjsCount nObjPlusTblsInfo)
    fstRemData = (head $ fstInstDat):(drop nstdObjsCount nObjPlusDat)