package is.L42.connected.withSafeOperators;
import java.security.AllPermission;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import ast.ExpCore.ClassB;
import ast.ExpCore.ClassB.*;
import ast.Ast;
import ast.Ast.Doc;
import ast.Ast.FieldDec;
import ast.Ast.Mdf;
import ast.Ast.MethodSelector;
import ast.Ast.MethodType;
import ast.Ast.NormType;
import ast.Ast.Position;
import ast.Ast.Type;
import auxiliaryGrammar.Functions;
import programReduction.Program;
import sugarVisitors.Desugar;
public class MakeKs {
public static ClassB makeKs(ClassB that,List<Ast.C> path, List<String> fieldNames,String mutK,String lentK,String readK,String immK,boolean fwd){
MakeKs m=new MakeKs();
if(path.isEmpty()){return m.makeKs(that,that,fieldNames,mutK,lentK,readK,immK,fwd);}
Errors42.checkExistsPathMethod(that, path, Optional.empty());
return that.onNestedNavigateToPathAndDo(path,
nc->Optional.of(nc.withInner(m.makeKs(that,(ClassB)nc.getInner(),fieldNames,mutK,lentK,readK,immK,fwd)))
);
}
private boolean hasReadLentSetter=false;
private ClassB makeKs(ClassB top, ClassB that,
List<String> fieldNames, String mutK, String lentK, String readK,String immK,boolean fwd) {
List<Type>fieldTypes=new ArrayList<>();
for(String f :fieldNames){
if(!MethodSelector.checkX(f,true)){throw new Error("Invalid field name provided:["+f+"]");}
fieldTypes.add(candidate(that.getMs(),f));
}
List<MethodWithType> toAdd=new ArrayList<>();
MethodWithType protoK = prototypeK(Doc.empty(),fieldNames,fieldTypes,that.getP());
if(fwd){protoK=changeMt(protoK,MakeKs::fwdK);}
MethodWithType _mutK = changeMt(protoK,MakeKs::mutK);
MethodWithType _lentK = changeMt(protoK,MakeKs::lentK);
MethodWithType _readK = changeMt(protoK,MakeKs::readK);
MethodWithType _immK = changeMt(protoK,MakeKs::immK);
if(!this.hasReadLentSetter){addWithName(mutK, toAdd, _mutK);}
addWithName(lentK, toAdd, _lentK);
addWithName(readK, toAdd, _readK);
addWithName(immK, toAdd, _immK);
List<Member> result=new ArrayList<>(that.getMs());
for(MethodWithType m:toAdd){
if(Functions.getIfInDom(result,m.getMs()).isPresent()){continue;}
result.add(m);
}
return that.withMs(result);
}
private void addWithName(String nameK, List<MethodWithType> toAdd, MethodWithType k) {
if(MethodSelector.checkX(nameK,true)){toAdd.add(k.withMs(k.getMs().withName(nameK)));}
}
static private MethodWithType changeMt(MethodWithType proto,Function<MethodType,MethodType>f) {
return proto.withMt(f.apply(proto.getMt()));
}
static private NormType mdfChange(Type n,Mdf m1,Mdf m2){
NormType nt=(NormType)n;
if(nt.getMdf().equals(m1)){return nt.withMdf(m2);}
return nt;
}
static private NormType addFwd(Type n){
return Functions.toPh(n.getNT());
}
static private MethodType fwdK(MethodType proto) {
return proto.withTs(proto.getTs().stream()
.map(MakeKs::addFwd).collect(Collectors.toList()));
}
static private MethodType mutK(MethodType proto) {
return proto.withTs(proto.getTs().stream()
.map(t->mdfChange(t,Mdf.Readable,Mdf.Mutable))
.collect(Collectors.toList()));
}
static private MethodType lentK(MethodType proto) {
return proto.withTs(proto.getTs().stream()
.map(t->mdfChange(t,Mdf.Mutable,Mdf.Lent)).
collect(Collectors.toList()))
.withReturnType(mdfChange(proto.getReturnType(),Mdf.Mutable,Mdf.Lent));
}
static private MethodType readK(MethodType proto) {
return proto.withTs(proto.getTs().stream()
.map(t->mdfChange(t,Mdf.Mutable,Mdf.Readable))
.map(t->mdfChange(t,Mdf.Lent,Mdf.Readable))
.collect(Collectors.toList()))
.withReturnType(mdfChange(proto.getReturnType(),Mdf.Mutable,Mdf.Readable));
}
static private MethodType immK(MethodType proto) {
return proto.withTs(proto.getTs().stream()
.map(t->{
NormType nt=(NormType)t;
if(!nt.getMdf().equals(Mdf.Class)){return nt.withMdf(Mdf.Immutable);}
return nt;
})
.collect(Collectors.toList()))
.withReturnType(mdfChange(proto.getReturnType(),Mdf.Mutable,Mdf.Immutable));
}
//can not reuse the desugar one, here we create ExpCore stuff, also , the sugar one may disappear
static private MethodWithType prototypeK(Doc doc,List<String>fieldNames,List<Type>fieldTypes,Position pos) {
MethodSelector ms=MethodSelector.of("",fieldNames);
NormType resT=NormType.mutThis0;
MethodType mt=new MethodType(false,ast.Ast.Mdf.Class,fieldTypes,resT,Collections.emptyList());
return new MethodWithType(doc, ms,mt, Optional.empty(),pos);
}
private ast.Ast.NormType candidate(List<Member> ms, String fName){
Optional<Member> a = Functions.getIfInDom(ms, MethodSelector.of(fName,Collections.singletonList("that")));
Optional<Member> b = Functions.getIfInDom(ms, MethodSelector.of("#"+fName,Collections.singletonList("that")));
Optional<Member> c = Functions.getIfInDom(ms, MethodSelector.of(fName,Collections.emptyList()));
Optional<Member> d = Functions.getIfInDom(ms, MethodSelector.of("#"+fName,Collections.emptyList()));
NormType ta=getType(a);
NormType tb=getType(b);
NormType tc=getType(c);
NormType td=getType(d);
ast.Ast.NormType res=null;
if (a.isPresent() && b.isPresent()){
if(!ta.equals(tb)){ throw new Error();}
}
if(a.isPresent()) {res=ta;}
else if(b.isPresent()){res=tb;}
if(res!=null){
if(c.isPresent() && !more(res,tc)){throw new Error();}
if(d.isPresent() && !more(res,td)){throw new Error();}
}
if(c.isPresent() && d.isPresent() && !compatible(tc,td)){throw new Error();}
if (res==null){res=moreSpecific(tc,td);}
if (res==null){throw new Error();}
if((a.isPresent() || b.isPresent()) && (res.getMdf().equals(Mdf.Lent)|| res.getMdf().equals(Mdf.Readable))){
this.hasReadLentSetter=true;}
if(!a.isPresent() && !b.isPresent() && res.getMdf().equals(Mdf.Lent)){
return res.withMdf(Mdf.Capsule);
}
return res;
}
private ast.Ast.NormType getType(Optional<Member>opt){
if(!opt.isPresent()){return null;}
Member m=opt.get();
MethodWithType mwt=(MethodWithType)m;
if(mwt.getMs().getNames().isEmpty()){return (NormType) mwt.getMt().getReturnType();}
return (NormType) mwt.getMt().getTs().get(0);
}
private boolean compatible(ast.Ast.NormType t1, ast.Ast.NormType t2){
if (!t1.getPath().equals(t2.getPath())){return false;}
if (Functions.isSubtype(t1.getMdf(),t2.getMdf())){return true;}
if (Functions.isSubtype(t2.getMdf(),t1.getMdf())){return true;}
return false;
}
private boolean more(ast.Ast.NormType t1, ast.Ast.NormType t2){
if (!t1.getPath().equals(t2.getPath())){return false;}
if (Functions.isSubtype(t1.getMdf(),t2.getMdf())){return true;}
return false;
}
private ast.Ast.NormType moreSpecific(ast.Ast.NormType t1, ast.Ast.NormType t2){
if(t1==null){return t2;}
if(t2==null){return t1;}
if (Functions.isSubtype(t1.getMdf(),t2.getMdf())){return t1;}
return t2;
}
}
/*
forall f in fs, get candidates
if ki name valid, generate ki using candiates, fs
if clone name valid, generate clone
if set lent/read exists, mutK will not be generated
if Ti is capsule or if only get and is lent, mutk will take capsule par.
*/