/* This overwrites the asType method in String to allow the conversion from
   a String to an EvalElement. Usage:
       "..." as ClassicalB converts "..." to a ClassicalB evaluation element
       "..." as EventB converts "..." to an EventB evaluation element
*/

def oldStringAsType = String.metaClass.getMetaMethod("asType", [Class] as Class[])
def oldArrayListAsType = ArrayList.metaClass.getMetaMethod("asType", [Class] as Class[])


String.metaClass.asType = { Class type -> 
       if (type == ClassicalB) return new ClassicalB(delegate) 
       if (type == EventB) return new EventB(delegate) 
       if (type == CSP) return new CSP(delegate)
       oldStringAsType.invoke(delegate, [type] as Class[])
}

ArrayList.metaClass.to { Class type ->
	def c = []
	if (type == ClassicalB ) delegate.each { c << (it as ClassicalB) }
	if (type == EventB ) delegate.each { c << (it as EventB) }
	if (type == CSP) delegate.each { c << (it as CSP) }
	return c
}

ArrayList.metaClass.asType = { Class type -> 
		if (type == Tuple) return new Tuple(delegate[0],delegate[1]) 
		def c = new Class[1]
        c[0] = type
        oldArrayListAsType.invoke(delegate, c)
}

/*Script.getMetaClass().transform = { a1,a2,a3=null ->
	def x
	if( a3 != null ) {
		x = new DynamicTransformer(a1,a2).with a3
	} else {
		if(a1 instanceof String) {
			x = new Transformer(a1).with a2 
		} else {
			d = []
			a1.each {
				d << "#r"+it.getId()
			}
			def r = com.google.common.base.Joiner.on(",").join(a1)
			x = new Transformer(r).with a2
		}
	}
	x
}*/

Script.getMetaClass().appendToTrace= { t,c ->
  def proxy = new TraceDecorator(t)
  c.resolveStrategy = Closure.DELEGATE_FIRST
  c.delegate = proxy
  c()
}


Script.getMetaClass().upgrade = { downloader.downloadCli(it) }
Script.getMetaClass().installCSPM = { downloader.installCSPM() }

/* Redirect print and println to our own buffered console*/
inConsole = true
Script.getMetaClass().print = { s -> __console.append(s.toString()); if (!inConsole) System.out.print(s) }
Script.getMetaClass().println = { s -> __console.append(s.toString()+"\n"); if (!inConsole) System.out.println(s)}
	
class TraceDecorator {
    private delegate
    private mch 
    def _ = null
  
    TraceDecorator(delegate) {
        this.delegate = delegate
        this.mch = delegate.getStateSpace().getMainComponent()
    }
    def invokeMethod(String name, args) {
        def pred = ""
        if (args.size() == 1 && args[0] instanceof Closure) {
           pred =  args[0]()         
        } 
        else {
           pred = make_predicate(mch.getOperations().find {it.name == name}.getParameters(),args)
        }
        
        this.delegate = delegate.invokeMethod(name, [pred])   
   }

  def make_predicate(formal_params,actual_params) {
   def  p = [formal_params,actual_params]
      .transpose()
      .findAll { a,b -> b != null }
      .collect { a,b -> a.toString() + "=" + b.toString() }
      .join(" & ")
    p.isEmpty() ? "TRUE = TRUE" : p
  }  
}

def execTrace(t,c) {
  proxy = new TraceDecorator(t)
  c.resolveStrategy = Closure.DELEGATE_FIRST
  c.delegate = proxy
  c()
}

def eval(script) {
	engine.eval(script)
}
