_ = require("underscore")
winston = require("winston")
util = require("util")

class Mapper 

  constructor: ->

  unmap: (schema) ->
    unmapped = 
      for schematic in schema
        do (schematic) ->
          if Array.isArray(schematic) 
            # replace value of keyed properties in schematic.original
            newArg = schematic.original
            for arg in schematic
              do (arg) ->
                newArg[arg.key] = arg.newvalue
            # return newArg
            JSON.stringify(newArg)

          else if typeof(schematic) is "object"
            schematic.newvalue.join(",")
    
    # return "/" joined string
    unmapped.join("/")


  # map supports three schemes:
  # map(["conf#genericlistid"], args)
  # map(["mapi#Organization.Id"], args) shorthanded to map(["Organization.Id"], args)
  # map([{ key1: obj1 }, ..., { keyN: objN}]) where
  #  key is the property of json argument which should be mapped, and
  #  obj is an object controlling the mapping. 
  # Example:
  # url is /ModuleX/ActionY/{ Organization: 123, Task: "Task.Oid1" }/999 which could be mapped as follows
  # map[{ Organization: { Type: "Organization", Property: "Id" }, Task: { Type: "Task", Property: "Oid" } }, "Personnel.Id"]
  # --
  # The following properties are available the obj controlling the mapi:
  #  * Type: The MAPI type of the object to map.
  #  * Property: The property to use on the object.
  #  * Depth: The depth used in the query (if you want to filter on nested properties)
  #  * Filter: A function taking two arguments, 1) the obj in question, 2) the schema used (here you can see e.g. other mapped properties). Return false to disallow object to be selected for mapping.
  map: (def, args) ->

    args = _.map((if typeof(args) is "string" then args.split("/") else args), (a) -> a.split(","))

    for arg, idx in args
      do (arg, idx) ->
        if def[idx]?

          if typeof(def[idx]) is "string" # for my beatiful and simple argument construction technique
            
            origin = "mapi"
            typeProperty = def[idx]

            # may contain information about origin
            if typeProperty.indexOf("#") > 0 then [origin, typeProperty] = def[idx].split("#")

            [type, property] = typeProperty.split(".")
            {
              origin: origin
              type: type
              property: property
              currentvalue: arg
              depth: 1
            }

          else if typeof(def[idx]) is "object" # for generic lists etc
            original = JSON.parse(arg)

            schematic = 
              for pKey, pTypeSelector of def[idx]
                do (pKey, pTypeSelector) ->
                  # get current value
                  currentvalue = original[pKey]
                  
                  if typeof(pTypeSelector) is "string"
                    [pType, pSelector] = pTypeSelector.split(".") # nb. we only support immediate properties
                    {
                      origin: "mapi"
                      type: pType
                      property: pSelector
                      currentvalue: currentvalue
                      key: pKey
                      depth: 1
                    }
                  else # assume pTypeSelector is object
                    {
                      origin: "mapi"
                      type: pTypeSelector.type
                      property: pTypeSelector.property
                      currentvalue: currentvalue
                      key: pKey
                      depth: pTypeSelector.depth
                      hql: pTypeSelector.hql
                      filter: pTypeSelector.filter
                    }

            schematic.original = original
            # return schematic
            schematic


        else # no def given
          {
            origin: "fixed"
            currentvalue: arg
          }

  applySchema: (method, action, fun, schema) -> @unmap(schema)


exports.mapper = Mapper