import {BaseParameter} from './RugOperation'
import {PathExpression} from '../tree/PathExpression'

//used by annotation functions

function set_metadata(obj: any, key: string, value: any){
  Object.defineProperty(obj, key, {value: value, writable: false, enumerable: true})
}


function get_metadata(obj: any, key: string){
   let desc =  Object.getOwnPropertyDescriptor(obj, key);
   if((desc == null || desc == undefined) && (obj.prototype != undefined)){
     desc = Object.getOwnPropertyDescriptor(obj.prototype, key);
   }
   if(desc != null || desc != undefined){
     return desc.value;
   }
   return null;
}


/**
* Decorator for parameters. Adds to object properties
*/
function Parameter(details: BaseParameter) {
  return function (target: any, propertyKey: string) {
    var params = get_metadata(target, "__parameters");
    if(params == null)

    details["name"] = propertyKey
    details["decorated"] = true
    params.push(details);
    set_metadata(target, "__parameters", params);
  }
}
/**
* Map a local field to some other cnofiguration item in a different system
**/
function MappedParameter(foreignKey: string) {
  return function (target: any, localKey: string) {
    var params = get_metadata(target, "__mappedParameters");
    if(params == null){
      params = []
    }
    var param = {localKey: localKey, foreignKey: foreignKey}
    params.push(param);
    set_metadata(target, "__mappedParameters", params);
  }
}

/**
* Decorator for editors. Use this intead of implementing the editor interface.
*/

function ruglike(fn: string, kind: string, msg: string) {
  return function (name: string, description: string) {
    return function(ctr: Function) {
      if (typeof ctr.prototype[fn] !== "function") {
        throw new Error(`${msg}`)
      }
      ctr.prototype["__name"] = name;
      ctr.prototype["__description"] = description
      ctr.prototype["__kind"] = kind
    }
  }
}

let Generator = ruglike("populate","generator", "populate must be a function with first parameter = Project")
let Reviewer = ruglike("review", "reviewer", "review must be a function with first parameter = Project")
let Editor = ruglike("edit", "editor", "edit must be a function with first parameter = Project")
let CommandHandler = ruglike("handle", "command-handler", "handle must be a function with first parameter = HandlerContext")
let ResponseHandler = ruglike("handle", "response-handler", "handle must be a function with first parameter = Response<T>")

let EventHandler = function (name: string, description: string, expression: PathExpression<any,any> | string) {
  return function(ctr: Function) {
    if (typeof ctr.prototype["handle"] !== "function") {
      throw new Error("handle must be a function with first parameter = Match<R,N>")
    }
    ctr.prototype["__name"] = name;
    ctr.prototype["__description"] = description
    ctr.prototype["__kind"] = "event-handler"
    if(typeof expression == 'string'){
      ctr.prototype["__expression"] = expression;
    }else{
      ctr.prototype["__expression"] = expression.expression;
    }
  }
}

/**
* Decorator for tags. Sets tags on the class
*/

function Tags(...tags: string[]) {
  return function(target: any) {
    target.prototype["__tags"] = tags
  }
}

function Intent(...intent: string[]) {
  return function(target: any) {
    target.prototype["__intent"] = intent
  }
}

function Secrets(...secrets: string[]) {
  return function(target: any) {
    if(target.prototype["__secrets"] == null) {
       target.prototype["__secrets"] = []
    }
    target.prototype["__secrets"] = target.prototype["__secrets"].concat(secrets);
  }
}

//for parameters to ResponseHandlers to do response body coercion
function ParseJson(target: Object, propertyKey: string | symbol, parameterIndex: number) {
  set_metadata(target, "__coercion", "JSON");
}

export {Parameter, Secrets, Tags, Intent, MappedParameter}
export {Editor, Generator, Reviewer}
export {ParseJson}
export {ResponseHandler, CommandHandler, EventHandler}

{
      params = []
    }