/* ************************************************************************
#
#  DivConq
#
#  http://divconq.com/
#
#  Copyright:
#    Copyright 2012 eTimeline, LLC. All rights reserved.
#
#  License:
#    See the license.txt file in the project's top-level directory for details.
#
#  Authors:
#    * Andy White
#
************************************************************************ */
package divconq.script;

import java.util.HashMap;
import java.util.Map;


import divconq.lang.OperationResult;
import divconq.schema.DataType;
import divconq.schema.SchemaManager;
import divconq.script.inst.CoreInstructionProvider;
import divconq.script.mutator.AddItemMutator;
import divconq.script.mutator.AddMutator;
import divconq.script.mutator.AppendMutator;
import divconq.script.mutator.ClearMutator;
import divconq.script.mutator.DecMutator;
import divconq.script.mutator.IncMutator;
import divconq.script.mutator.Lower;
import divconq.script.mutator.RemoveFieldMutator;
import divconq.script.mutator.RemoveItemMutator;
import divconq.script.mutator.Replace;
import divconq.script.mutator.SetFieldMutator;
import divconq.script.mutator.SetMutator;
import divconq.script.mutator.Substring;
import divconq.script.mutator.Trim;
import divconq.script.mutator.Upper;
import divconq.script.mutator.ValidateMutator;
import divconq.struct.Struct;
import divconq.xml.XElement;

public class ActivityManager {
	protected SchemaManager schema = null;
	protected Map<String, IMutator> mutateProviders = new HashMap<String, IMutator>();
	//protected Map<String, ITypeProvider> typeProviders = new HashMap<String, ITypeProvider>();
	protected Map<String, IInstructionProvider> instProviders = new HashMap<String, IInstructionProvider>();
	
	public SchemaManager getSchema() {
		return this.schema;
	}
	
	public void setSchema(SchemaManager v) {
		this.schema = v;
	}
	
	public ActivityManager() {
		// TODO load from config
		
		this.mutateProviders.put("Set", new SetMutator());
		this.mutateProviders.put("Append", new AppendMutator());
		this.mutateProviders.put("Trim", new Trim());
		this.mutateProviders.put("Upper", new Upper());
		this.mutateProviders.put("Lower", new Lower());
		this.mutateProviders.put("Substring", new Substring());
		this.mutateProviders.put("Replace", new Replace());
		this.mutateProviders.put("Add", new AddMutator());
		this.mutateProviders.put("Dec", new DecMutator());
		this.mutateProviders.put("Inc", new IncMutator());

		this.mutateProviders.put("AddItem", new AddItemMutator());
		this.mutateProviders.put("RemoveItem", new RemoveItemMutator());
		this.mutateProviders.put("Clear", new ClearMutator());
		this.mutateProviders.put("SetField", new SetFieldMutator());
		this.mutateProviders.put("RemoveField", new RemoveFieldMutator());
		this.mutateProviders.put("Validate", new ValidateMutator());
		
		IInstructionProvider cip = new CoreInstructionProvider();
		this.instProviders.put("Main", cip);
		this.instProviders.put("Function", cip);
		this.instProviders.put("CallFunction", cip);
		this.instProviders.put("Var", cip);
		this.instProviders.put("Global", cip);
		this.instProviders.put("With", cip);
		this.instProviders.put("For", cip);
		this.instProviders.put("Debug", cip);
		this.instProviders.put("Info", cip);
		this.instProviders.put("Warn", cip);
		this.instProviders.put("Error", cip);
		this.instProviders.put("While", cip);
		this.instProviders.put("Until", cip);
		this.instProviders.put("If", cip);
		this.instProviders.put("Switch", cip);
		this.instProviders.put("Case", cip);
		this.instProviders.put("Break", cip);
		this.instProviders.put("Continue", cip);
		this.instProviders.put("Exit", cip);
	}
	
	// create anonymous var
	public Struct createVariable(OperationResult mr, String type) {
		DataType mut = this.schema.getType(type);
		
		if (mut == null)
			return null;
		
		Struct ret = mut.create(mr);
		
		if (ret == null)
			return null;
		
		return ret;
	}
	
	public DataType getType(String type) {
		return this.schema.getType(type);
	}
	
	public Instruction createInstruction(XElement def) {
		IInstructionProvider mut = this.instProviders.get(def.getName());
		return (mut != null) ? mut.createInstruction(def) : null;		
	}

	public void mutate(Activity activity, StackEntry stack, Struct target, XElement code) {
		IMutator mut = this.mutateProviders.get(code.getName());

		if (mut != null) 
			mut.mutate(activity, stack, code, target);
	}
	
	/*
	 * goals
	 * x		better compiler
	 * x		no exceptions
	 * x		rewrite stack control
	 * 		advanced data types
	 * 		create functions
	 * 		globals
	 * x		improve flexible attributes 
	 * x		new source handling
	 * 		more instructions 
	 * 		component management
	 * 		freeze/thaw
	 * 		create libraries (call by function)
	 * 		create libraries (call by script)
	 * 
	 * 		when a var goes out of scope, call dispose  (close streams, sources, dests, remove temp files, etc)
	 * 
	 * 		check user level within instructions - error if user is not authorized
	 * 
	 * 
	 * value/var attributes
	 * 		"literal"
	 * 		"literal string with {$variable} in it"
	 * 
	 * 		` forces literal, so
	 * 		"`hello world"	resolves to "hello world"
	 * 		"``hello"		resolves to "`hello"
	 * 		"`$x"			resolves to "$x"
	 * 
	 * 		$ forces variable named after value
	 * 		"$x"			reference to x
	 * 		
	 * 		$$ forces variable by the name held in value
	 * 		"$$z"			resolves to reference to "x" if z = "x"
	 * 
		<Main Steps="[int][optional]">
			... insts
		</Main>
		
		<With Target="[string]" SetTo="[ref][optional]">
			... mutators
		</With>
		
		<Var Name="[string]" Type="[string]" SetTo="[ref][optional]">
			... mutators
		</Var>
		
			Composite Set/Merge - defaults to Xml if any non-text children or to Json if text only children
			
		<Var Name="msg1" Type="ResultMessage">
			<Merge Format"Xml,Yaml,Json">
				<Record>
					<Field Name="Kind" Value="Level" />
					<Field Name="Code" Value="3" />
					<Field Name="Message" Value="Howday partner!" />
				</Record>
			</Merge>
		</Var>
		
		<For Name="[string][optional]" From="[int][optional, default 0]" To="[int][optional, default max long]" Step="[int][optional, default 1]">
			... insts
		</For>
		
		<Debug Code="[int][optional]">
			message [string]
		</Debug>
		
		<Info Code="[int][optional]">
			message [string]
		</Info>
		
		<Warn Code="[int][optional]">
			message [string]
		</Warn>
		
		<Error Code="[int][optional]">
			message [string]
		</Error>

		**** logic **** 
		
		// Not may appear on any Logic or Condition
		<Logic Target="[ref][optional]" Equal|Equals="[reference]" Not="[boolean][optional]">
			... insts
		</Logic>

		<Logic Target="[ref][optional]" LessThan="[reference]">
			... insts
		</Logic>

		<Logic Target="[ref][optional]" GreaterThan="[reference]">
			... insts
		</Logic>

		<Logic Target="[ref][optional]" LessThanOrEqual="[reference]">
			... insts
		</Logic>

		<Logic Target="[ref][optional]" GreaterThanOrEqual="[reference]">
			... insts
		</Logic>

		// may combine expressions
		<Logic Target="[ref][optional]" GreaterThanOrEqual="[reference]" LessThan="[reference]">
			... insts
		</Logic>

		// condition with Name ignores the top Name, condition without Name uses top Name in compare
		<Logic Target="[ref][optional]">
			<Detail>		// automatically an AND expression
				<Condition ...>
				<And/Or>
					<Condition Target="[ref][optional]" ... any of the conditions above ...>
					<Condition ...>
				</And/Or>
				<Condition ...>
			</Detail>
			
			... insts
		</Logic>

		// if no conditions listed then just check to see if this variable - exists, is not null, not false and not empty string
		<Logic Target="[ref][optional]">
			... insts
		</Logic>
		
		// if no name then it is treated as CommandState
		<Logic>
			... insts
		</Logic>
		
		**** end logic **** 

		<While [any logic]>
			... insts
		</While>

		<Until [any logic]>
			... insts
		</Until>

		<If [any logic]>
			... insts
		</If>

		<Switch Target="[ref][optional]">
			... Case insts
		</Switch>

		<Case [any logic]>
			... insts
		</Case>
		
		<Break />
		
		<Continue />
		
		<Exit Code="[int]" Message="[string]" />

		<IfLastFlagSuccess>
			... insts
		</IfLastFlagSuccess>

		<IfLastFlagFail>
			... insts
		</IfLastFlagFail>

		// forces "^_" into Name attribute
		<IfLastResult [any logic]>
			... insts
		</IfLastResult>

		<IfExitFlagSuccess>
			... insts
		</IfExitFlagSuccess>

		<IfExitFlagFail>
			... insts
		</IfExitFlagFail>
		
		// we are an async language - don't really wait - instead schedule a call back in N seconds
		<Sleep Seconds="[int]" Period="ISO Period" Duration="dur" />
		
		<Progress Step="[int]" Name="[string]" Amount="[int]">
			message
		</Progress>
		
		- NO <Milestone Name="[string]" Release="[int]" Acquire="[int]" />
		
		// milestone handling
		<Await Name="[string]" />
		
		<Release Name="[string]" />
		
		// we are an async language - don't really wait - instead service call back triggers our call back
		<CallService Name="[string]" Feature="[string][optional]" Op="[string][optional]" Hub="[string]" Await="[boolean][optional, def true]" ResultTo="var to set - or just use default[ref][optional]" ResultCodeTo="...[int][optional]">
			<Args>
				<Field ... />
			</Args>
		</CallService>
		
		// execute an "program" which only sends back a code and a result
		// entire execution is one call, we use an async call back 
		// may report to collab for debugging though
		<CallScript Name="[string]" Hub="[string][optional]" Await="[boolean][optional, def true]" ResultTo="var to set - or just use default[ref][optional]" ResultCodeTo="...[int][optional]">
			<Args>
				<Field ... />
			</Args>
		</CallScript>
		
		** CallScript and CallService can both direct response to a mailbox if Await false this gets aysnc flow, if True just means to look in the mailbox for response in addition to _LastResult
		
		// execution is inline and we watch it become part of our stack - heavy but easier to debug
		<CallFunction Name="[string]" Library="lib.name.here [string][optional]" ResultTo="var to set - or just use default[ref][optional]" ResultCodeTo="...[int][optional]">
			<Args>
				<Field ... />
			</Args>
		</CallFunction>
		
		<Args> can be inline:
		
		<Args>
			<Field ... />
		</Args>
		
		or reference
		
		<Args Name="name of var [string]" />
		
		-------------------------------------------------------------------
		
		Examples
		
		<Script>
		<Types>
			... special types not in Repo, but useful to local - type validation is ensured (Strict) or warned (Permissive)
		</Types>

		// functions (local or lib) present a stack barrier, searches for variable names stop here
		
		<Function Name="" ParamType="AnyRecord or specific" ReturnType="AnyRecord or specific">
		</Function>
		
		... or can define inline
		
		<Function Name="">
			<ParamType Inherits="">
				...field defs... 
			</ParamType>
			
			<ReturnType Inherits="">
				...field defs... 
			</ReturnType>
		</Function>
		
		<Main>
			<ParamType Inherits="">
				...field defs... 
			</ParamType>
			
			<ReturnType Inherits="">
				...field defs... 
			</ReturnType>
		
		
			<Var Name="x" Type="Integer" SetTo="5">
				<Add Value="2" />
				<Inc />
				<Random From="x" To="y" />
			</Var>
		
			<List Name="y">
				<Scalar Value="2" />
				<Scalar Value="^x" />
				<Record>
					<Field Name="">
						scalar, record, list
					</Field>
				</Record>
			</List>
		
			<With Name="y">
				<RemoveItem Index="2" />
				<AddItem>
					<Scalar ... />
				</AddItem>
				<Clear />
				<InsertItem Index="0">
					<Scalar ... />
				</InsertItem>
			</With>
		
			<Record Name="z">
				<Field Name="">
					scalar, record, list
				</Field>
			</Record>
			
			// change string at 3rd position of property a (list)
			<With Name="z.a[2]">
				<Concat Value="nuy" />
			</With>
			
			<With Name="z">
				set field, remove field
			</With>
			
			<Component Name="var name" Class="id string - ftcFtp">
				<Settings>
					...
				</Settings>
			</Component>
		
			<For Name="bottles" From="4" To="0" Step="-1">
				<Switch Name="bottles">
				<Case GreaterThan="1">
					<Var Name="bottlesleft" Type="Integer" Value="{$bottles}">
						<Dec />
					</Var>

					<Info>
						{$bottles} bottles of beer on the wall, {$bottles} bottles of beer.
						Take one down and pass it around, {$bottlesleft} bottles of beer on the wall.
					</Info>
				</Case>
				<Case Equal="1">
					<Info>
						{$bottles} bottle of beer on the wall, {$bottles} bottle of beer.
						Take one down and pass it around, no more bottles of beer on the wall.
					</Info>
				</Case>
				<Case Equal="0">
					<Info>
						No more bottles of beer on the wall, no more bottles of beer.
						Go to the store and buy some more, 99 bottles of beer on the wall.
					</Info>
				</Case>
				</Switch>
			</For>
		</Main>
		</Script>

		<Var Name="strm1" Class="divconq.core.StreamController">
			<dccTransform>
				compress 
				encrypt
			</dccTransform>
		</Var>
		
		<Var Name="disk1" Class="divconq.protos.DiskController">
			<dcpChangeDirectory Path="./files/accounts" />
			<dcpGet Stream="$strm1" Match="*.xls" />
		</Var>		
				
		<Var Name="sftp1" Class="divconq.protos.SftpController">
			<dcpSettings>
				<Field ...
			</dcpSettings>
			<dcpSignOn User="" Password="" Key="" Cert="" />
			<dcpChangeDirectory Path="./accountbackup" />
			<dcpPut Stream="$strm1" />
			<dcpExit />
		</Var>
		
		<Var Name="disk1">
			<dcpExit />
		</Var>		

		
	 * 
	 */
}
