// Copyright 2009-2010 Bluespec, Inc.  All rights reserved.
// $Revision$
// $Date$

package ModuleContext;

import ModuleContextCore::*;
import HList::*;
import Vector::*;

export ModuleContextCore::*;
export ModuleContext::*;

//typedef 4 CLOCKCONTEXTSIZE;
typedef 9 CLOCKCONTEXTSIZE;

typeclass Context#(type mc1, type c2);
   module [mc1] getContext(c2) provisos (IsModule#(mc1, a));
   module [mc1] putContext#(c2 s)(Empty) provisos (IsModule#(mc1, a));
endtypeclass

instance Context#(Module, void);
   module [Module] getContext(st1);
      return ?;
   endmodule
   module [Module] putContext#(void c)(Empty);
   endmodule
endinstance

instance Context#(ModuleContext#(st1), st1);
   module [ModuleContext#(st1)] getContext(st1);
      let c <- getCompleteContext();
      return c;
   endmodule
   module [ModuleContext#(st1)] putContext#(st1 c1)(Empty);
      putCompleteContext(c1);
   endmodule
endinstance

instance Context#(ModuleContext#(st1), st2)
   provisos (Gettable#(st1, st2));
   module [ModuleContext#(st1)] getContext(st2);
      let c <- getCompleteContext();
      return getIt(c);
   endmodule
   module [ModuleContext#(st1)] putContext#(st2 c2)(Empty);
      let c <- getCompleteContext();
      putCompleteContext(putIt(c, c2));
   endmodule
endinstance

// ---

module [mc1] applyToContext#(function c2 f(c2 c))(Empty)
   provisos (IsModule#(mc1, a), Context#(mc1, c2));

   c2 x <- getContext();
   putContext(f(x));
endmodule

module [mc1] applyToContextM#(function module#(c2) m(c2 c))(Empty)
   provisos (IsModule#(mc1, a), Context#(mc1, c2));

   c2 x <- getContext();
   c2 y <- m(x);
   putContext(y);
endmodule

// ======

typedef struct {
   Vector#(n, Clock) clks;
   Vector#(n, Reset) rsts;
		} ClockContextP#(numeric type n);

ClockContextP#(n) initClockContext = ClockContextP {
   clks: replicate(noClock), rsts: replicate(noReset) };

typedef ClockContextP#(CLOCKCONTEXTSIZE) ClockContext;
typedef Empty ClockContextIfc;
typedef ModuleContext#(HList1#(ClockContext))     ClockModule;

module mkInitialClockContextWithClocks#(Vector#(CLOCKCONTEXTSIZE, Clock) cs,
					Vector#(CLOCKCONTEXTSIZE, Reset) rs)(ClockContext);
   return ClockContextP {clks: cs, rsts: rs };
endmodule

// ======

typeclass Expose#(type c, type ifc, numeric type n)
   dependencies (c determines (ifc, n));
   module unburyContext#(c x)(ifc);
      return error("unburyContext not defined for some typeclass");
   endmodule
   module unburyContextWithClocks#(c x, ClockContextP#(n) cc)(ifc);
      (*hide*)
      let _ifc <- unburyContext(x);
      return _ifc;
   endmodule
endtypeclass

instance Expose#(HList1#(ct1), ifc1, n)
   provisos (Expose#(ct1,ifc1,n));

   module unburyContext#(HList1#(ct1) c1)(ifc1);
      (*hide_all*) ifc1 _i1 <- unburyContext(hHead(c1));
      return _i1;
   endmodule
   module unburyContextWithClocks#(HList1#(ct1) c1, ClockContextP#(n) cc)(ifc1);
      (*hide_all*) ifc1 _i1 <- unburyContextWithClocks(hHead(c1), cc);
      return _i1;
   endmodule
endinstance

instance Expose#(HCons#(c1,c2), Tuple2#(ifc1,ifc2), n)
   provisos (Expose#(c1,ifc1,n), Expose#(c2,ifc2,n));

   module unburyContext#(HCons#(c1,c2) c12)(Tuple2#(ifc1,ifc2));
      (*hide_all*) ifc1 _i1 <- unburyContext(hHead(c12));
      (*hide_all*) ifc2 _i2 <- unburyContext(hTail(c12));
      return tuple2(_i1, _i2);
   endmodule
   module unburyContextWithClocks#(HCons#(c1,c2) c12, ClockContextP#(n) cc)(Tuple2#(ifc1,ifc2));
      (*hide_all*) ifc1 _i1 <- unburyContextWithClocks(hHead(c12), cc);
      (*hide_all*) ifc2 _i2 <- unburyContextWithClocks(hTail(c12), cc);
      return tuple2(_i1, _i2);
   endmodule
endinstance

instance Expose#(ClockContextP#(n), Empty, n);
   module unburyContext#(ClockContextP#(n) x)();
   endmodule
   module unburyContextWithClocks#(ClockContextP#(n) x, ClockContextP#(m) cc)();
   endmodule
endinstance

// ====

typeclass Hide#(type mc, type ifc);
   module [mc] reburyContext#(ifc i)(Empty);
endtypeclass

instance Hide#(mc, Empty)
   provisos (IsModule#(mc, a));
   module [mc] reburyContext#(Empty i)(Empty);
   endmodule
endinstance

instance Hide#(mc, Tuple2#(ifc1,ifc2))
   provisos (IsModule#(mc, a),
	     Hide#(mc,ifc1), Hide#(mc,ifc2));

   module [mc] reburyContext#(Tuple2#(ifc1,ifc2) i12)(Empty);
      match {.x1, .x2} = i12;
      reburyContext(x1);
      reburyContext(x2);
   endmodule
endinstance

// ====

typeclass ContextRun#(type m, type c1, type ctx2)
   dependencies ((m, c1) determines ctx2);
   module [m] runWithContext #(c1 initState, ModuleContext#(ctx2, ifcType) mkI)
        (Tuple2#(c1, ifcType));
endtypeclass

typeclass ContextsRun#(type m, type c1, type ctx2)
   dependencies ((m, c1) determines ctx2);
   module [m] runWithContexts#(c1 initState, ModuleContext#(ctx2, ifcType) mkI)
	(Tuple2#(c1, ifcType));
endtypeclass

instance ContextRun#(ModuleContext#(ctx), c1, HCons#(c1,ctx));
   module [ModuleContext#(ctx)] runWithContext#(c1 initState,
					     ModuleContext#(HCons#(c1, ctx), ifcType) mkI)
      (Tuple2#(c1, ifcType));

      ctx c0 <- getContext();
      (*hide*)
      Tuple2#(HCons#(c1, ctx), ifcType) _res <-
         liftModule(runWithCompleteContext(hCons(initState, c0), mkI));
      match {.ctx_fin, .ifc} = _res;
      putContext(hTail(ctx_fin));
      return tuple2(hHead(ctx_fin), ifc);
   endmodule
endinstance

instance ContextsRun#(ModuleContext#(ctx), c1, ctx2)
   provisos (HAppend#(c1, ctx, ctx2),
	     HSplit# (ctx2, c1, ctx));
   module [ModuleContext#(ctx)] runWithContexts#(c1 initState,
						 ModuleContext#(ctx2, ifcType) mkI)
      (Tuple2#(c1, ifcType));

      ctx c0 <- getContext();
      (*hide*)
      Tuple2#(ctx2, ifcType) _res <-
          liftModule(runWithCompleteContext(hAppend(initState, c0), mkI));
      match {.c1ctx_fin, .ifc} = _res;
      Tuple2#(c1, ctx) c1ctx= hSplit(c1ctx_fin);
      match {.c1_fin, .ctx_fin} = c1ctx;
      putContext(ctx_fin);
      return tuple2(c1_fin, ifc);
   endmodule
endinstance

instance ContextRun#(Module, c1, c1);
   module [Module] runWithContext#(c1 initState,
				   ModuleContext#(c1, ifcType) mkI)
      (Tuple2#(c1, ifcType));

      (*hide*)
      Tuple2#(c1, ifcType) _res <-
         liftModule(runWithCompleteContext(initState, mkI));
      return _res;
   endmodule
endinstance


instance ContextsRun#(Module, c1, c1);
   module [Module] runWithContexts#(c1 initState,
				    ModuleContext#(c1, ifcType) mkI)
      (Tuple2#(c1, ifcType));

      (*hide*)
      Tuple2#(c1, ifcType) _res <-
          liftModule(runWithCompleteContext(initState, mkI));
      return _res;
   endmodule
endinstance


// ====

module [Module] unbury#(c1 initState,
			ModuleContext#(c1, ifcType) mkM)(Tuple2#(ifc, ifcType))
   provisos (Expose#(c1,ifc,n));

   (*hide*)
   Tuple2#(c1, ifcType) ci <- runWithCompleteContext(initState, mkM);
   match {.c, .i} = ci;
   (*hide*)
   ifc i2 <- unburyContext(c);
   return tuple2(i2, i);
endmodule

module [Module] unburyWithClocks#(c1 initState, ModuleContext#(c1, ifcType) mkM)(Tuple2#(ifc, ifcType))
   provisos (Expose#(c1,ifc,n), Context#(ModuleContext#(c1), ClockContextP#(n)),
	     Gettable#(c1, ClockContextP#(n)));

   (*hide*)
   Tuple2#(c1, ifcType) ci <- runWithCompleteContext(initState, mkM);
   match {.c, .i} = ci;
   ClockContextP#(n) cc = getIt(c);
   (*hide*)
   ifc i2 <- unburyContextWithClocks(c, cc);
   return tuple2(i2, i);
endmodule

module [c1] rebury#(Module#(Tuple2#(ifc, ifcType)) mkI)(ifcType)
   provisos (IsModule#(c1, a), Hide#(c1, ifc));
   match {.c, .i} <- liftModule(mkI);
   reburyContext(c);
   return i;
endmodule

module [c1] reburyWithClocks#(function Module#(Tuple2#(ifc, ifcType)) mkI
				 (Vector#(n, Clock) cks, Vector#(n, Reset) rs))(ifcType)
   provisos (IsModule#(c1, a), Hide#(c1, ifc), Context#(c1, ClockContextP#(n)));
   ClockContextP#(n) clks <- getContext;
   match {.c, .i} <- liftModule(mkI(clks.clks, clks.rsts));
   (*hide_all*) let rbc <- reburyContext(c);
   return i;
endmodule

endpackage
