import java.util.*;
import java.util.logging.*;
import java.util.function.*;

public class ASTShareL extends ASTNode {

    String sh;
    ASTNode lhs;
    ASTNode rhs;

    public ASTShareL(String _sh, ASTNode _lhs,ASTNode _rhs) {
        sh = _sh;
        lhs = _lhs;
        rhs = _rhs;
    }

    public  void ASTInsertPipe(Function<ASTNode,ASTNode> f, ASTNode from) throws Exception
    {
	if (from==lhs) { ASTNode nnode = f.apply(from);
	    lhs.setanc(nnode);
	    lhs = nnode;
	    nnode.setanc(this);
	} else if (from==rhs) { ASTNode nnode = f.apply(from);
	    rhs.setanc(nnode);
	    rhs = nnode;
	    nnode.setanc(this);
	} else
	    {
		throw new Exception("ASTInsertPipe: call not expected");   
	    }
    }

    public void ASTInsertUse(String ch, ASTType t, ASTNode here, Boolean disCont) throws Exception
    {
        anc.ASTInsertUse(ch, t, this, disCont);
    }

    public  void ASTInsertCall(String ch, String cho, ASTType t, ASTNode here) throws Exception
    {
        ASTNode pushCall = new ASTCall(ch, cho, t, here);
        pushCall.eg = eg;

        here.setanc(pushCall);
        pushCall.setanc(this);
        if (lhs==here) {
            lhs = pushCall;
        }
        else {	rhs = pushCall;
        }
    }

    public void ASTupdCont(ASTNode newCont, ASTNode caller) throws Exception{
        if(caller == lhs)
            lhs = newCont;
        else
            rhs = newCont;
    }

    public void ASTInsertWhyNot(String ch, ASTType _t, ASTNode here) throws Exception
    {
        anc.ASTInsertWhyNot(ch, _t, this);
    }

    public ASTNode ASTweakeningOnLeaf(String ch, ASTType typ, boolean exp) throws Exception
    {
	Set<String> s = lhs.fn(new HashSet<String>());
	if ( s.contains(ch)  ) {
	    lhs = lhs.ASTweakeningOnLeaf(ch,typ, exp);
	    return this;
	};
	rhs = rhs.ASTweakeningOnLeaf(ch,typ, exp);
	return this;
    }

    public void typecheck(Env<ASTType> ed, Env<ASTType> eg, Env<EnvEntry> ep) throws Exception {
        this.eg = eg;

        ASTType ty = ed.find(sh);

        ty = ty.unfoldType(ep);
        if (ty instanceof ASTUsageLT) {
            ASTUsageLT tys = (ASTUsageLT)ty;
	    if (tys.islin())
		throw new TypeError("Line " + lineno + " :" +"SHAREL: "+sh+" is not of USAGEL.");
            Env<ASTType> eglhs = eg.assoc("$DUMMY",new ASTBotT());
            lhs.typecheck(ed,eglhs,ep);
            lhs.linclose(ed,ep);
            ed.upd(sh,new ASTUsageT(tys.getin(),false));
            Env<ASTType> egrhs = eg.assoc("$DUMMY",new ASTBotT());
            rhs.typecheck(ed,egrhs,ep);
            rhs.linclose(ed,ep);
        }
        else throw new TypeError("Line " + lineno + " :" +"SHAREL: "+sh+" is not of USAGEL.");
    }

    public Set<String> fn(Set<String> s) {
        s = lhs.fn(s);
        s = rhs.fn(s);
        s.add(sh);
        return s;
    }

    public Set<String> fnLinear(Set<String> s) {
        s = lhs.fnLinear(s);
        s = rhs.fnLinear(s);
        s.add(sh);
        return s;
    }


    public ASTNode subst(Env<ASTType> e) {
        ASTShareL p = new ASTShareL(sh,lhs.subst(e),rhs.subst(e));
 	p.lhs.setanc(p);
	p.rhs.setanc(p);
	return p;
    }

    public void subs(String x, String y){// implements x/y (substitutes y by x)
        if(y == sh)
            sh = x;

        lhs.subs(x,y);
        rhs.subs(x,y);
    }
    public void runproc(Env<EnvEntry> ep, Env<LinSession> ed, Env<Server> eg, Logger logger) throws Exception{
        LinSession session = (LinSession) ed.find(sh);

        session.incUsages(1);

        CLLSj.threadPool.submit(
				new Runnable(){
				    public void run(){ try {
					    lhs.runproc(ep, ed, eg, logger);
					} catch (Exception e) {e.printStackTrace(System.out);} }
				});
        CLLSj.threadPool.submit(
				new Runnable(){
				    public void run(){ try {
					    rhs.runproc(ep, ed, eg, logger);
					} catch (Exception e) {e.printStackTrace(System.out);} }
				});

    }
}
