type store = Store.t
type substore = Substore.t
type action = Store.action
type record = Store.record
type choice = Choice.t
type proc = Choice.proc
type value = Choice.value
type heap = Heap.t
type environment = Environment.t
type pattern = Environment.pattern
type definition = Environment.definition
type species = Species.t

type t = {
  environment: environment;
  counter: int;
  heap: heap;
  store: store;
  time: float
}
let get_environment (t:t) = t.environment
let get_heap (t:t) = t.heap

let empty = {
  environment = Environment.empty;
  counter = 0;
  heap = Heap.empty;
  store = Store.empty;
  time = 0.
}

let init (e:environment) = {empty with environment=e}

let display (html:bool) (t:t) = 
  let newline = if html then "<br>" else "\n" in
    newline ^ "*Counter: " ^ string_of_int t.counter ^ 
    newline ^ "*Time: " ^ string_of_float t.time ^
    newline ^ "*Environment:" ^ newline ^ Environment.display html t.environment ^
    newline ^ newline ^ "*Store:" ^ newline ^ Store.display html t.store ^
    newline ^ newline ^ "*Heap:" ^ newline ^ Heap.display html t.heap 

let to_string (t:t) = display false t
let to_html (t:t) = display true t

let fresh (t:t) = 
  let t = {t with counter = t.counter+1}
  in t.counter,t

let remove_delay (x:value) (index:float) (t:t) = match Store.lookup_delay x index t.store with
    index,None -> None
  | index,Some(i) ->   match Heap.remove i t.heap with
	None -> None
      | Some(h,s,c) -> match Choice.find_action index (Action.Delay(x)) c with 
	    None -> None
	  | Some(index,v,k,p) -> Some(v,k,p,{t with heap = h; store = Store.minus t.time i s t.store})

let remove_output (x:value) (index:float) (t:t) = match Store.lookup_output x index t.store with
    index,None ->  None
  | index,Some(i) ->   match Heap.remove i t.heap with
	None ->  None
      | Some(h,s,c) -> match Choice.find_action index (Action.Output(x,[],Value.empty)) c with 
	    None -> None
	  | Some(index,v,k,p) -> Some(v,k,p,{t with heap = h; store = Store.minus t.time i s t.store})

let remove_input (x:value) (index:float) (t:t) = match Store.lookup_input x index t.store with
    index,None -> None
  | index,Some(external_outputs,i) ->   match Heap.remove i t.heap with
	None ->  None
      | Some(h,s,c) -> match Choice.find_action (index/.external_outputs) (Action.Input(x,[],Value.empty)) c with 
	    None -> None
	  | Some(index',v,k,p) -> 
	      let output_index = index/.index'
	      in Some(v,k,p,output_index,{t with heap = h; store = Store.minus t.time i s t.store})

let add (i:species) (c:choice) (t:t) = match Heap.find i t.heap with
    Some(counter,s,c) -> {t with heap = Heap.add i (counter+1,s,c) t.heap ; store=Store.plus t.time i s t.store}
  | None -> 
      let s:substore = Choice.create_substore c
      in {t with heap = Heap.add i (1,s,c) t.heap ; store=Store.plus t.time i s t.store}

let define (n:value) (m:pattern list) (d:definition) (t:t) = {t with environment = Environment.add n m d t.environment}
let find (n:value) (t:t) = Environment.find n t.environment
let gillespie (t:t) = Store.gillespie t.store
let flip (time:float) (v:value) (t:t) = {t with store = Store.flip v t.store; time = time}
let plot (ks:action list) (is:species list) (t:t) = Store.plot ks t.store @ Heap.plot is t.heap
let debug (time:float) (t:t) = string_of_float time ^ "\t" ^ Store.to_string t.store ^ "\n"

(*****************************************************************************) 

