{-# LANGUAGE QuasiQuotes #-}

module Objc (
    objcTests,
    objcRegressionTests
  ) where

import Test.Framework
import Test.Framework.Providers.HUnit
import Test.HUnit (Assertion, (@?=))

import Language.C.Quote.ObjC

objcTests :: Test
objcTests = testGroup "Objective-C"
    [ testCase "Objective-C params" objcProp
    , testCase "Objective-C property" objcDict
    , testCase "Objective-C method parameters" objcParam
    , testCase "Objective-C method definition" objcMethodDefinition
    , testCase "Objective-C classmethod" objcArgumentCls
    , testCase "Objective-C argument" objcArgument
    , testCase "Objective-C arguments" objcArguments
    , testCase "Objective-C varargument" objcVarArgument
    , testCase "Objective-C literals" objcLits
    ]
  where
    objcDict :: Assertion
    objcDict =
        [cexp| @{$dictelems:(elems [("a","b"),("c", "d")])} |] @?= [cexp| @{@"a" : @"b",@"c": @"d"}|]
      where
        elems = map (\(k,v) -> [objcdictelem|$exp:(objcLit k) : $exp:(objcLit v)|] )

    objcProp :: Assertion
    objcProp =
        [cedecl|
         @interface Foo
         - (void) foo;
         $prop:propdec1
         $props:propdec2
         $prop:propdec3
         @end
         |]

        @?=

        [cedecl|
        @interface Foo
        - (void) foo;
        @property (nonatomic, retain) int i;
        @property (nonatomic, retain) float j;
        @property (nonatomic, retain) char k;
        @property (nonatomic) double l;
        @end
        |]
      where
        propdec n typ = [objcprop|@property ($propattrs:r) $ty:typ $id:n;|]
        propdec' n typ = [objcprop|@property ($propattr:p) $ty:typ $id:n;|]
        p = [objcpropattr|nonatomic|]
        q = [objcpropattr|retain|]
        r = [p,q]
        propdec1 = propdec "i" [cty|int|]
        propdec2 = map (\(n,t) -> propdec n t) [("j", [cty|float|]), ("k", [cty|char|])]
        propdec3 = propdec' "l" [cty|double|]

    objcParam :: Assertion
    objcParam =
        [cedecl|
        @interface Foo
        - (void) $methparams:paramNew ;
        $methproto:val ;
        @end
        |]

        @?=

        [cedecl|
        @interface Foo
        - (void) foo:(int)str fo:(int)str1;
        + (int) test1:(int)str2;
        @end
        |]
      where
        paramNew1 = [objcmethparam|$id:("foo"):(int)str |]
        paramNew2 = [objcmethparam|fo:(int)str1 |]
        paramNew3 = [objcmethparam|test1:(int)str2 |]
        paramNew = [paramNew1, paramNew2]
        val = [objcmethproto|+ (int) $methparam:paramNew3|]

    objcMethodDefinition :: Assertion
    objcMethodDefinition =
        [cedecl|
        @implementation fooclass
        $methdefs:(val)
        $methdef:(val3)
        @end
        |]

        @?=

        [cedecl|
        @implementation fooclass
        + (int) test1:(int)foo { }
        - (char) test2:(char)bar { }
        + (float) test3:(double)baz { }
        @end
        |]
      where
        val3 = [objcmethdef|+ (float) $methparam:paramNew5 {} |]
        paramNew5 = [objcmethparam|test3:(double)baz |]
        val2 = [objcmethdef|+ (int) $methparam:paramNew3 {} |]
        paramNew3 = [objcmethparam|test1:(int)foo |]
        val1 = [objcmethdef|- (char) $methparam:paramNew4 {} |]
        paramNew4 = [objcmethparam|test2:(char)bar |]
        val = [val2, val1]

    objcArgumentCls :: Assertion
    objcArgumentCls =
        [citem|[somename test];|] @?= [citem|[$recv:(k) $id:("test")];|]
      where
        k = [objcmethrecv|somename|]

    objcArgument :: Assertion
    objcArgument =
        [citem|[$recv:(k) $kwarg:(p)];|] @?= [citem|[somename doSome:@"string"];|]
      where
        k = [objcmethrecv|somename|]
        p = [objcarg|doSome:@"string"|]

    objcArguments :: Assertion
    objcArguments =
        [citem|[$recv:(k) $kwargs:(r)];|]
        @?= [citem|[somename doSome:@"string" doSomeMore:@"moreStrings"];|]
      where
        k = [objcmethrecv|somename|]
        p = [objcarg|doSome:@"string"|]
        q = [objcarg|doSomeMore:@"moreStrings"|]
        r = [p,q]

    objcVarArgument :: Assertion
    objcVarArgument =
        [citem|[$recv:(k) $kwarg:(r) $args:(p)];|]
        @?= [citem|[NSString stringWithFormat:@"A string: %@, a float: %1.2f", @"string", 31415.9265];|]
      where
        k = [objcmethrecv|NSString|]
        r = [objcarg|stringWithFormat:@"A string: %@, a float: %1.2f"|]
        p = [a, b]
        a = [cexp|@"string"|]
        b = [cexp|31415.9265|]

    objcLits :: Assertion
    objcLits =
        [cexp|@[$(objcLit "foo"), $(objcLit True), $(objcLit False), $(objcLit 'a'), nil]|]
        @?= [cexp|@[@"foo", @YES, @NO, @'a', nil]|]

objcRegressionTests :: Test
objcRegressionTests = testGroup "Objective-C Regressions"
    [ testCase "Issue #51" issue51 ]
  where
    issue51 :: Assertion
    issue51 = [cunit|
          @interface $id:prefixedClassName : NSObject
          $ifdecls:ifaceDecls
          @end
        |]

        @?= [cunit|
          @interface dummyClass : NSObject
          - (void) foo:(int)str fo:(int)str1;
          + (int) test1:(int)str2;
          @end
        |]
      where
        prefixedClassName :: String
        prefixedClassName = "dummyClass"

        ifaceDecls = [objcifdecls|
            - (void) foo:(int)str fo:(int)str1;
            + (int) test1:(int)str2;
            |]