package com.stuffwithstuff.magpie.interpreter;

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

import com.stuffwithstuff.magpie.ast.Field;
import com.stuffwithstuff.magpie.intrinsic.ClassNew;

public class EnvironmentBuilder {
  public EnvironmentBuilder(Interpreter interpreter) {
    mInterpreter = interpreter;
    mModule = mInterpreter.getBaseModule();
  }

  public ClassObj createClassClass() {
    Scope scope = mModule.getScope();
    
    // The class of all classes. Its class is itself.
    // TODO(bob): Doc.
    ClassObj classObj = new ClassObj(null, Name.CLASS, null,
        new HashMap<String, FieldObj>(), "");
    classObj.bindClass(classObj);
    scope.define(false, Name.CLASS, classObj);
    
    return classObj;
  }
  
  public void initialize() {
    // Create the default new() method for creating objects.
    Scope scope = mModule.getScope();
    Callable newMethod = new ClassNew(scope);
    scope.define("new", newMethod);
    
    ClassObj iterable = class_("Iterable").end();
    ClassObj indexable = class_("Indexable", iterable).end();
    ClassObj comparable = class_("Comparable").end();
    
    class_("Array", indexable).end();
    class_("Bool").end();
    class_("Int", comparable).end();
    class_("Function").end();
    class_("List", indexable).end();
    class_("Nothing").end();
    class_("Record").end();
    class_("String", comparable, indexable).end();
    class_("Tuple", indexable).end();

    // Define the core error classes.
    ClassObj error = class_("Error").end();
    
    class_(Name.AMBIGUOUS_METHOD_ERROR, error).end();
    class_(Name.ARGUMENT_ERROR, error).end();
    // TODO(bob): Move into io module.
    class_(Name.IO_ERROR, error).end();
    class_(Name.NO_MATCH_ERROR, error).end();
    class_(Name.NO_METHOD_ERROR, error).end();
    class_(Name.NO_VARIABLE_ERROR, error).end();
    class_(Name.OUT_OF_BOUNDS_ERROR, error).end();
    class_(Name.PARENT_COLLISION_ERROR, error).end();
    class_(Name.PARSE_ERROR, error).end();
    class_(Name.REDEFINITION_ERROR, error).end();
    class_(Name.INITIALIZATION_ERROR, error).end();
  }
  
  private ClassBuilder class_(String name, ClassObj... parents) {
    return new ClassBuilder(name, parents);
  }
  
  private class ClassBuilder {
    public ClassBuilder(String name, ClassObj... parents) {
      mName = name;
      mParents = parents;
    }
    
    public ClassObj end() {
      // TODO(bob): Doc.
      ClassObj classObj = mInterpreter.createClass(mName,
          Arrays.asList(mParents), mFields, mModule.getScope(), "");
      mModule.getScope().define(false, mName, classObj);
      
      return classObj;
    }
    
    private final String mName;
    private final ClassObj[] mParents;
    private final Map<String, Field> mFields = new HashMap<String, Field>();
  }
  
  private final Interpreter mInterpreter;
  private final Module mModule;
}
