{-|
Module      : Language.Python.Optics.Newlines
Copyright   : (C) CSIRO 2017-2019
License     : BSD3
Maintainer  : Isaac Elliott <isaace71295@gmail.com>
Stability   : experimental
Portability : non-portable
-}
module Language.Python.Optics.Newlines where

import Control.Lens.Traversal (Traversal')
import Data.List.NonEmpty (NonEmpty(..))

import qualified Data.FingerTree as FingerTree

import Language.Python.Syntax

{-

I can't derive this with generic-lens :( it wants generic instances for certain
things that can't have those instance (and don't contain newlines anyways)

-}
class HasNewlines s where
  -- | 'Traversal'' targeting all of thie 'Newline's in a structure
  --
  -- This only targets places that contain the 'Newline' datatype; it doesn't target
  -- newline characters in string literals, for example.
  _Newlines :: Traversal' s Newline

instance (HasNewlines a, HasNewlines b) => HasNewlines (a, b) where
  _Newlines f (a, b) = (,) <$> _Newlines f a <*> _Newlines f b

instance (HasNewlines a, HasNewlines b, HasNewlines c) => HasNewlines (a, b, c) where
  _Newlines f (a, b, c) =
    (,,) <$>
    _Newlines f a <*>
    _Newlines f b <*>
    _Newlines f c

instance (HasNewlines a, HasNewlines b, HasNewlines c, HasNewlines d) => HasNewlines (a, b, c, d) where
  _Newlines f (a, b, c, d) =
    (,,,) <$>
    _Newlines f a <*>
    _Newlines f b <*>
    _Newlines f c <*>
    _Newlines f d

instance HasNewlines (e v a) => HasNewlines (ImportAs e v a) where
  _Newlines f (ImportAs a b c) =
    ImportAs a <$>
    _Newlines f b <*>
    _Newlines f c

instance HasNewlines (RelativeModuleName v a) where
  _Newlines f (RelativeWithName ann a b) =
    RelativeWithName ann <$>
    _Newlines f a <*>
    _Newlines f b
  _Newlines f (Relative ann a) = Relative ann <$> _Newlines f a

instance (HasNewlines a, HasNewlines b) => HasNewlines (Either a b) where
  _Newlines f (Left a) = Left <$> _Newlines f a
  _Newlines f (Right a) = Right <$> _Newlines f a

instance HasNewlines Newline where
  _Newlines = id

instance HasNewlines a => HasNewlines [a] where
  _Newlines = traverse._Newlines

instance HasNewlines Whitespace where
  _Newlines _ Space = pure Space
  _Newlines _ Tab = pure Tab
  _Newlines f (Continued nl ws) = Continued <$> f nl <*> _Newlines f ws
  _Newlines _ (Comment c) = pure $ Comment c
  _Newlines f (Newline nl) = Newline <$> f nl

instance HasNewlines (Blank a) where
  _Newlines f (Blank a b c) = (\b' -> Blank a b' c) <$> _Newlines f b

instance HasNewlines (Block v a) where
  _Newlines f (Block a b c) =
    Block <$>
    _Newlines f a <*>
    _Newlines f b <*>
    _Newlines f c

instance HasNewlines Colon where
  _Newlines f (MkColon a) = MkColon <$> _Newlines f a

instance HasNewlines Dot where
  _Newlines f (MkDot a) = MkDot <$> _Newlines f a

instance HasNewlines Comma where
  _Newlines f (MkComma a) = MkComma <$> _Newlines f a

instance HasNewlines At where
  _Newlines f (MkAt a) = MkAt <$> _Newlines f a

instance HasNewlines (Semicolon a) where
  _Newlines f (MkSemicolon a b) = MkSemicolon a <$> _Newlines f b

instance HasNewlines Equals where
  _Newlines f (MkEquals a) = MkEquals <$> _Newlines f a

instance HasNewlines (Suite v a) where
  _Newlines f (SuiteOne a b c) = SuiteOne a b <$> _Newlines f c
  _Newlines f (SuiteMany a b c d e) =
    (\b' d' e' -> SuiteMany a b' c d' e') <$>
    _Newlines f b <*>
    f d <*>
    _Newlines f e

instance HasNewlines Indent where
  _Newlines f (MkIndent a) = MkIndent <$> (FingerTree.traverse'._Newlines) f a

instance HasNewlines (Indents a) where
  _Newlines f (Indents a b) = (\a' -> Indents a' b) <$> _Newlines f a

instance HasNewlines (UnOp a) where
  _Newlines f (Negate a b) = Negate a <$> _Newlines f b
  _Newlines f (Positive a b) = Positive a <$> _Newlines f b
  _Newlines f (Complement a b) = Complement a <$> _Newlines f b

instance HasNewlines (BinOp a) where
  _Newlines f x =
    case x of
      Is a b -> Is a <$> _Newlines f b
      IsNot a b c -> IsNot a <$> _Newlines f b <*> _Newlines f c
      In a b -> In a <$> _Newlines f b
      NotIn a b c -> NotIn a <$> _Newlines f b <*> _Newlines f c
      Minus a b -> Minus a <$> _Newlines f b
      Exp a b -> Exp a <$> _Newlines f b
      BoolAnd a b -> BoolAnd a <$> _Newlines f b
      BoolOr a b -> BoolOr a <$> _Newlines f b
      Eq a b -> Eq a <$> _Newlines f b
      Lt a b -> Lt a <$> _Newlines f b
      LtEq a b -> LtEq a <$> _Newlines f b
      Gt a b -> Gt a <$> _Newlines f b
      GtEq a b -> GtEq a <$> _Newlines f b
      NotEq a b -> NotEq a <$> _Newlines f b
      Multiply a b -> Multiply a <$> _Newlines f b
      Divide a b -> Divide a <$> _Newlines f b
      FloorDivide a b -> FloorDivide a <$> _Newlines f b
      Percent a b -> Percent a <$> _Newlines f b
      Plus a b -> Plus a <$> _Newlines f b
      BitOr a b -> BitOr a <$> _Newlines f b
      BitXor a b -> BitXor a <$> _Newlines f b
      BitAnd a b -> BitAnd a <$> _Newlines f b
      ShiftLeft a b -> ShiftLeft a <$> _Newlines f b
      ShiftRight a b -> ShiftRight a <$> _Newlines f b
      At a b -> At a <$> _Newlines f b

instance HasNewlines a => HasNewlines (CommaSep a) where
  _Newlines f = go
    where
      go CommaSepNone = pure CommaSepNone
      go (CommaSepOne a) = CommaSepOne <$> _Newlines f a
      go (CommaSepMany a b c) =
        CommaSepMany <$>
        _Newlines f a <*>
        _Newlines f b <*>
        go c

instance HasNewlines a => HasNewlines (CommaSep1 a) where
  _Newlines f = go
    where
      go (CommaSepOne1 a) = CommaSepOne1 <$> _Newlines f a
      go (CommaSepMany1 a b c) =
        CommaSepMany1 <$>
        _Newlines f a <*>
        _Newlines f b <*>
        go c

instance HasNewlines a => HasNewlines (CommaSep1' a) where
  _Newlines f = go
    where
      go (CommaSepOne1' a b) = CommaSepOne1' <$> _Newlines f a <*> _Newlines f b
      go (CommaSepMany1' a b c) =
        CommaSepMany1' <$>
        _Newlines f a <*>
        _Newlines f b <*>
        go c

instance HasNewlines (Ident v a) where
  _Newlines f (MkIdent a b c) = MkIdent a b <$> _Newlines f c

instance HasNewlines a => HasNewlines (Maybe a) where
  _Newlines = traverse._Newlines

instance HasNewlines (Param v a) where
  _Newlines f (PositionalParam a b c) =
    PositionalParam a <$>
    _Newlines f b <*>
    _Newlines f c
  _Newlines f (KeywordParam a b c d e) =
    KeywordParam a <$>
    _Newlines f b <*>
    _Newlines f c <*>
    _Newlines f d <*>
    _Newlines f e
  _Newlines f (StarParam a b c d) =
    StarParam a <$>
    _Newlines f b <*>
    _Newlines f c <*>
    _Newlines f d
  _Newlines f (UnnamedStarParam a b) =
    UnnamedStarParam a <$>
    _Newlines f b
  _Newlines f (DoubleStarParam a b c d) =
    DoubleStarParam a <$>
    _Newlines f b <*>
    _Newlines f c <*>
    _Newlines f d

instance HasNewlines (Arg v a) where
  _Newlines f (PositionalArg a b) =
    PositionalArg a <$>
    _Newlines f b
  _Newlines f (KeywordArg a b c d) =
    KeywordArg a <$>
    _Newlines f b <*>
    _Newlines f c <*>
    _Newlines f d
  _Newlines f (StarArg a b c) =
    StarArg a <$>
    _Newlines f b <*>
    _Newlines f c
  _Newlines f (DoubleStarArg a b c) =
    DoubleStarArg a <$>
    _Newlines f b <*>
    _Newlines f c

instance HasNewlines (CompFor v a) where
  _Newlines f (CompFor a b c d e) =
    CompFor a <$>
    _Newlines f b <*>
    _Newlines f c <*>
    _Newlines f d <*>
    _Newlines f e

instance HasNewlines (CompIf v a) where
  _Newlines f (CompIf a b c) =
    CompIf a <$>
    _Newlines f b <*>
    _Newlines f c

instance HasNewlines (e v a) => HasNewlines (Comprehension e v a) where
  _Newlines f (Comprehension a b c d) =
    Comprehension a <$>
    _Newlines f b <*>
    _Newlines f c <*>
    _Newlines f d

instance HasNewlines (TupleItem v a) where
  _Newlines f (TupleItem a b) = TupleItem a <$> _Newlines f b
  _Newlines f (TupleUnpack a b c d) =
    TupleUnpack a <$>
    _Newlines f b <*>
    _Newlines f c <*>
    _Newlines f d

instance HasNewlines (ListItem v a) where
  _Newlines f (ListItem a b) = ListItem a <$> _Newlines f b
  _Newlines f (ListUnpack a b c d) =
    ListUnpack a <$>
    _Newlines f b <*>
    _Newlines f c <*>
    _Newlines f d

instance HasNewlines (SetItem v a) where
  _Newlines f (SetItem a b) = SetItem a <$> _Newlines f b
  _Newlines f (SetUnpack a b c d) =
    SetUnpack a <$>
    _Newlines f b <*>
    _Newlines f c <*>
    _Newlines f d

instance HasNewlines (DictItem v a) where
  _Newlines f (DictItem a b c d) =
    DictItem a <$>
    _Newlines f b <*>
    _Newlines f c <*>
    _Newlines f d
  _Newlines f (DictUnpack a b c) =
    DictUnpack a <$>
    _Newlines f b <*>
    _Newlines f c

instance HasNewlines (Subscript v a) where
  _Newlines f (SubscriptExpr a) = SubscriptExpr <$> _Newlines f a
  _Newlines f (SubscriptSlice a b c d) =
    SubscriptSlice <$>
    _Newlines f a <*>
    _Newlines f b <*>
    _Newlines f c <*>
    _Newlines f d

instance HasNewlines a => HasNewlines (NonEmpty a) where
  _Newlines f (a :| as) = (:|) <$> _Newlines f a <*> _Newlines f as

instance HasNewlines (StringLiteral a) where
  _Newlines = stringLiteralWhitespace.traverse._Newlines

instance HasNewlines (Expr v a) where
  _Newlines fun = go
    where
      go e =
        case e of
          Unit a b c -> Unit a <$> _Newlines fun b <*> _Newlines fun c
          Lambda a b c d e ->
            Lambda a <$>
            _Newlines fun b <*>
            _Newlines fun c <*>
            _Newlines fun d <*>
            go e
          Yield a b c ->
            Yield a <$> _Newlines fun b <*> _Newlines fun c
          YieldFrom a b c d ->
            YieldFrom a <$> _Newlines fun b <*> _Newlines fun c <*> go d
          Ternary a b c d e f ->
            Ternary a <$>
            go b <*>
            _Newlines fun c <*>
            go d <*>
            _Newlines fun e <*>
            go f
          ListComp a b c d ->
            ListComp a <$>
            _Newlines fun b <*>
            _Newlines fun c <*>
            _Newlines fun d
          List a b c d ->
            List a <$>
            _Newlines fun b <*>
            _Newlines fun c <*>
            _Newlines fun d
          DictComp a b c d ->
            DictComp a <$>
            _Newlines fun b <*>
            _Newlines fun c <*>
            _Newlines fun d
          Dict a b c d ->
            Dict a <$>
            _Newlines fun b <*>
            _Newlines fun c <*>
            _Newlines fun d
          SetComp a b c d ->
            SetComp a <$>
            _Newlines fun b <*>
            _Newlines fun c <*>
            _Newlines fun d
          Set a b c d ->
            Set a <$>
            _Newlines fun b <*>
            _Newlines fun c <*>
            _Newlines fun d
          Deref a b c d ->
            Deref a <$>
            go b <*>
            _Newlines fun c <*>
            _Newlines fun d
          Subscript a b c d e ->
            Subscript a <$>
            go b <*>
            _Newlines fun c <*>
            _Newlines fun d <*>
            _Newlines fun e
          Call a b c d e ->
            Call a <$>
            go b <*>
            _Newlines fun c <*>
            _Newlines fun d <*>
            _Newlines fun e
          None a b -> None a <$> _Newlines fun b
          Ellipsis a b -> Ellipsis a <$> _Newlines fun b
          BinOp a b c d ->
            BinOp a <$>
            go b <*>
            _Newlines fun c <*>
            go d
          UnOp a b c ->
            UnOp a <$>
            _Newlines fun b <*>
            go c
          Parens a b c d ->
            Parens a <$>
            _Newlines fun b <*>
            go c <*>
            _Newlines fun d
          Ident a b -> Ident a <$> _Newlines fun b
          Int a b c -> Int a b <$> _Newlines fun c
          Float a b c -> Float a b <$> _Newlines fun c
          Imag a b c -> Imag a b <$> _Newlines fun c
          Bool a b c -> Bool a b <$> _Newlines fun c
          String a b -> String a <$> _Newlines fun b
          Tuple a b c d ->
            Tuple a <$>
            _Newlines fun b <*>
            _Newlines fun c <*>
            _Newlines fun d
          Not a b c -> Not a <$> _Newlines fun b <*> go c
          Generator a b -> Generator a <$> _Newlines fun b
          Await a b c -> Await a <$> _Newlines fun b <*> _Newlines fun c

instance HasNewlines (Decorator v a) where
  _Newlines fun (Decorator a b c d e f g) =
    Decorator a <$>
    _Newlines fun b <*>
    _Newlines fun c <*>
    _Newlines fun d <*>
    pure e <*>
    fun f <*>
    _Newlines fun g

instance HasNewlines (ExceptAs v a) where
  _Newlines f (ExceptAs a b c) = ExceptAs a <$> _Newlines f b <*> _Newlines f c

instance HasNewlines (WithItem v a) where
  _Newlines f (WithItem a b c) = WithItem a <$> _Newlines f b <*> _Newlines f c

instance HasNewlines (CompoundStatement v a) where
  _Newlines fun s =
    case s of
      Fundef ann decos idnt asyncWs ws1 name ws2 params ws3 mty s ->
        Fundef ann <$>
        _Newlines fun decos <*>
        _Newlines fun idnt <*>
        _Newlines fun asyncWs <*>
        _Newlines fun ws1 <*>
        _Newlines fun name <*>
        _Newlines fun ws2 <*>
        _Newlines fun params <*>
        _Newlines fun ws3 <*>
        _Newlines fun mty <*>
        _Newlines fun s
      If ann idnt ws1 cond s elifs els ->
        If ann <$>
        _Newlines fun idnt <*>
        _Newlines fun ws1 <*>
        _Newlines fun cond <*>
        _Newlines fun s <*>
        _Newlines fun elifs <*>
        _Newlines fun els
      While ann idnt ws1 cond s els ->
        While ann <$>
        _Newlines fun idnt <*>
        _Newlines fun ws1 <*>
        _Newlines fun cond <*>
        _Newlines fun s <*>
        _Newlines fun els
      TryExcept ann idnt b c f k l ->
        TryExcept ann <$>
        _Newlines fun idnt <*>
        _Newlines fun b <*>
        _Newlines fun c <*>
        _Newlines fun f <*>
        _Newlines fun k <*>
        _Newlines fun l
      TryFinally ann idnt b c idnt2 f g ->
        TryFinally ann <$>
        _Newlines fun idnt <*>
        _Newlines fun b <*>
        _Newlines fun c <*>
        _Newlines fun idnt2 <*>
        _Newlines fun f <*>
        _Newlines fun g
      For ann idnt asyncWs b c d e f g ->
        For ann <$>
        _Newlines fun idnt <*>
        _Newlines fun asyncWs <*>
        _Newlines fun b <*>
        _Newlines fun c <*>
        _Newlines fun d <*>
        _Newlines fun e <*>
        _Newlines fun f <*>
        _Newlines fun g
      ClassDef a decos idnt b c d e ->
        ClassDef a <$>
        _Newlines fun decos <*>
        _Newlines fun idnt <*>
        _Newlines fun b <*>
        _Newlines fun c <*>
        _Newlines fun d <*>
        _Newlines fun e
      With a b asyncWs c d e ->
        With a <$>
        _Newlines fun b <*>
        _Newlines fun asyncWs <*>
        _Newlines fun c <*>
        _Newlines fun d <*>
        _Newlines fun e

instance HasNewlines (ModuleName v a) where
  _Newlines f = go
    where
      go (ModuleNameOne a b) =
        ModuleNameOne a <$> _Newlines f b
      go (ModuleNameMany a b c d) =
        ModuleNameMany a <$> _Newlines f b <*> _Newlines f c <*> go d

instance HasNewlines (ImportTargets v a) where
  _Newlines f (ImportAll a b) =
    ImportAll a <$> _Newlines f b
  _Newlines f (ImportSome a b) =
    ImportSome a <$> _Newlines f b
  _Newlines f (ImportSomeParens a b c d) =
    ImportSomeParens a <$>
    _Newlines f b <*>
    _Newlines f c <*>
    _Newlines f d

instance HasNewlines (SimpleStatement v a) where
  _Newlines fun s =
    case s of
      Return a b c -> Return a <$> _Newlines fun b <*> _Newlines fun c
      Expr a b -> Expr a <$> _Newlines fun b
      Assign a b c -> Assign a <$> _Newlines fun b <*> _Newlines fun c
      AugAssign a b c d ->
        AugAssign a <$>
        _Newlines fun b <*>
        pure c <*>
        _Newlines fun d
      Pass a b -> Pass a <$> _Newlines fun b
      Break a b -> Break a <$> _Newlines fun b
      Continue a b -> Continue a <$> _Newlines fun b
      Global a b c -> Global a <$> _Newlines fun b <*> _Newlines fun c
      Nonlocal a b c -> Nonlocal a <$> _Newlines fun b <*> _Newlines fun c
      Del a b c -> Del a <$> _Newlines fun b <*> _Newlines fun c
      Import a b c ->
        Import a <$>
        _Newlines fun b <*>
        _Newlines fun c
      From a b c d e ->
        From a <$>
        _Newlines fun b <*>
        _Newlines fun c <*>
        _Newlines fun d <*>
        _Newlines fun e
      Raise a b c ->
        Raise a <$>
        _Newlines fun b <*>
        _Newlines fun c
      Assert a b c d ->
        Assert a <$>
        _Newlines fun b <*>
        _Newlines fun c <*>
        _Newlines fun d

instance HasNewlines (SmallStatement v a) where
  _Newlines f (MkSmallStatement s ss sc cmt nl) =
    MkSmallStatement <$>
    _Newlines f s <*>
    _Newlines f ss <*>
    _Newlines f sc <*>
    pure cmt <*>
    _Newlines f nl

instance HasNewlines (Statement v a) where
  _Newlines f (CompoundStatement c) =
    CompoundStatement <$> _Newlines f c
  _Newlines f (SmallStatement i a) =
    SmallStatement <$>
    _Newlines f i <*>
    _Newlines f a

instance HasNewlines (Module v a) where
  _Newlines f = go
    where
      go ModuleEmpty = pure ModuleEmpty
      go (ModuleBlankFinal a) = pure $ ModuleBlankFinal a
      go (ModuleBlank a b c) =
        ModuleBlank a <$> f b <*> go c
      go (ModuleStatement a b) =
        ModuleStatement <$> _Newlines f a <*> go b
