import { convertOtherProperties } from './utils.js';

class Arrhenius {
  #keys = ['A', 'B', 'C', 'D', 'E', 'Ea', 'reactants', 'products', 'name', 'gas_phase'];
  constructor(params) {
    this.A = params['A'] || 1.0;
    this.B = params['B'] || 0.0;
    this.C = params['C'] || 0.0;
    this.D = params['D'] || 300.0;
    this.E = params['E'] || 0.0;
    // C and Ea are mutually exclusive
    this.Ea = params['Ea'];
    this.reactants = params['reactants'];
    this.products = params['products'];
    this.name = params['name'];
    this.gas_phase = params['gas_phase'];
    this.other_properties = {};
    Object.entries(params).forEach(([key, value]) => {
      if (this.#keys.includes(key) == false) {
        this.other_properties[key] = value;
      }
    });
  }
  getJSON() {
    let obj = {};
    obj['type'] = 'ARRHENIUS';
    obj['name'] = this.name;
    obj['A'] = this.A;
    obj['B'] = this.B;
    if (this.Ea === undefined) obj['C'] = this.C;
    else obj['Ea'] = this.Ea;
    obj['D'] = this.D;
    obj['E'] = this.E;
    obj['gas phase'] = this.gas_phase;
    obj['reactants'] = this.reactants.map((r) => r.getJSON());
    obj['products'] = this.products.map((p) => p.getJSON());
    const ops = convertOtherProperties(this.other_properties);
    Object.assign(obj, ops);
    return obj;
  }
}

class Branched {
  #keys = [
    'X',
    'Y',
    'a0',
    'n',
    'reactants',
    'nitrate_products',
    'alkoxy_products',
    'name',
    'gas_phase',
  ];
  constructor(params) {
    this.X = params['X'];
    this.Y = params['Y'];
    this.a0 = params['a0'];
    this.n = params['n'];
    this.reactants = params['reactants'];
    this.nitrate_products = params['nitrate_products'];
    this.alkoxy_products = params['alkoxy_products'];
    this.name = params['name'];
    this.gas_phase = params['gas_phase'];
    this.other_properties = {};
    Object.entries(params).forEach(([key, value]) => {
      if (this.#keys.includes(key) == false) {
        this.other_properties[key] = value;
      }
    });
  }
  getJSON() {
    let obj = {};
    obj['type'] = 'BRANCHED_NO_RO2';
    obj['name'] = this.name;
    obj['X'] = this.X;
    obj['Y'] = this.Y;
    obj['a0'] = this.a0;
    obj['n'] = this.n;
    obj['gas phase'] = this.gas_phase;
    obj['reactants'] = this.reactants.map((r) => r.getJSON());
    obj['nitrate products'] = this.nitrate_products.map((p) => p.getJSON());
    obj['alkoxy products'] = this.alkoxy_products.map((p) => p.getJSON());
    const ops = convertOtherProperties(this.other_properties);
    Object.assign(obj, ops);
    return obj;
  }
}

class Emission {
  #keys = ['scaling_factor', 'products', 'name', 'gas_phase'];
  constructor(params) {
    this.scaling_factor = params['scaling_factor'] || 1.0;
    this.products = params['products'];
    this.name = params['name'];
    this.gas_phase = params['gas_phase'];
    this.other_properties = {};
    Object.entries(params).forEach(([key, value]) => {
      if (this.#keys.includes(key) == false) {
        this.other_properties[key] = value;
      }
    });
  }
  getJSON() {
    let obj = {};
    obj['type'] = 'EMISSION';
    obj['name'] = this.name;
    obj['scaling factor'] = this.scaling_factor;
    obj['gas phase'] = this.gas_phase;
    obj['products'] = this.products.map((p) => p.getJSON());
    const ops = convertOtherProperties(this.other_properties);
    Object.assign(obj, ops);
    return obj;
  }
}

class FirstOrderLoss {
  #keys = ['scaling_factor', 'reactants', 'name', 'gas_phase'];
  constructor(params) {
    this.scaling_factor = params['scaling_factor'] || 1.0;
    this.reactants = params['reactants'];
    this.name = params['name'];
    this.gas_phase = params['gas_phase'];
    this.other_properties = {};
    Object.entries(params).forEach(([key, value]) => {
      if (this.#keys.includes(key) == false) {
        this.other_properties[key] = value;
      }
    });
  }
  getJSON() {
    let obj = {};
    obj['type'] = 'FIRST_ORDER_LOSS';
    obj['name'] = this.name;
    obj['scaling factor'] = this.scaling_factor;
    obj['gas phase'] = this.gas_phase;
    obj['reactants'] = this.reactants.map((r) => r.getJSON());
    const ops = convertOtherProperties(this.other_properties);
    Object.assign(obj, ops);
    return obj;
  }
}

class Photolysis {
  #keys = ['scaling_factor', 'reactants', 'products', 'name', 'gas_phase'];
  constructor(params) {
    this.scaling_factor = params['scaling_factor'] || 1.0;
    this.reactants = params['reactants'];
    this.products = params['products'];
    this.name = params['name'];
    this.gas_phase = params['gas_phase'];
    this.other_properties = {};
    Object.entries(params).forEach(([key, value]) => {
      if (this.#keys.includes(key) == false) {
        this.other_properties[key] = value;
      }
    });
  }
  getJSON() {
    let obj = {};
    obj['type'] = 'PHOTOLYSIS';
    obj['name'] = this.name;
    obj['scaling factor'] = this.scaling_factor;
    obj['gas phase'] = this.gas_phase;
    obj['reactants'] = this.reactants.map((r) => r.getJSON());
    obj['products'] = this.products.map((p) => p.getJSON());
    const ops = convertOtherProperties(this.other_properties);
    Object.assign(obj, ops);
    return obj;
  }
}

class Surface {
  #keys = ['reaction_probability', 'gas_phase_species', 'gas_phase_products', 'name', 'gas_phase'];
  constructor(params) {
    this.reaction_probability = params['reaction_probability'] || 1.0;
    this.gas_phase_species = params['gas_phase_species'];
    this.gas_phase_products = params['gas_phase_products'];
    this.name = params['name'];
    this.gas_phase = params['gas_phase'];
    this.other_properties = {};
    Object.entries(params).forEach(([key, value]) => {
      if (this.#keys.includes(key) == false) {
        this.other_properties[key] = value;
      }
    });
  }
  getJSON() {
    let obj = {};
    obj['type'] = 'SURFACE';
    obj['name'] = this.name;
    obj['reaction probability'] = this.reaction_probability;
    obj['gas phase'] = this.gas_phase;
    // b/c Kyle said so ??
    obj['gas-phase species'] = this.gas_phase_species.species_name;
    obj['gas-phase products'] = this.gas_phase_products.map((p) => p.getJSON());
    const ops = convertOtherProperties(this.other_properties);
    Object.assign(obj, ops);
    return obj;
  }
}

class TaylorSeries {
  #keys = [
    'A',
    'B',
    'C',
    'D',
    'E',
    'Ea',
    'taylor_coefficients',
    'reactants',
    'products',
    'name',
    'gas_phase',
  ];
  constructor(params) {
    this.A = params['A'] || 1.0;
    this.B = params['B'] || 0.0;
    this.C = params['C'] || 0.0;
    this.D = params['D'] || 300.0;
    this.E = params['E'] || 0.0;
    // C and Ea are mutually exclusive
    this.Ea = params['Ea'];
    this.taylor_coefficients = params['taylor_coefficients'] || [1.0];
    this.reactants = params['reactants'];
    this.products = params['products'];
    this.name = params['name'];
    this.gas_phase = params['gas_phase'];
    this.other_properties = {};
    Object.entries(params).forEach(([key, value]) => {
      if (this.#keys.includes(key) == false) {
        this.other_properties[key] = value;
      }
    });
  }
  getJSON() {
    let obj = {};
    obj['type'] = 'TAYLOR_SERIES';
    obj['name'] = this.name;
    obj['A'] = this.A;
    obj['B'] = this.B;
    if (this.Ea === undefined) obj['C'] = this.C;
    else obj['Ea'] = this.Ea;
    obj['D'] = this.D;
    obj['E'] = this.E;
    obj['taylor coefficients'] = this.taylor_coefficients;
    obj['gas phase'] = this.gas_phase;
    obj['reactants'] = this.reactants.map((r) => r.getJSON());
    obj['products'] = this.products.map((p) => p.getJSON());
    const ops = convertOtherProperties(this.other_properties);
    Object.assign(obj, ops);
    return obj;
  }
}

class Troe {
  #keys = [
    'k0_A',
    'k0_B',
    'k0_C',
    'kinf_A',
    'kinf_B',
    'kinf_C',
    'Fc',
    'N',
    'reactants',
    'products',
    'name',
    'gas_phase',
  ];
  constructor(params) {
    this.k0_A = params['k0_A'] || 1.0;
    this.k0_B = params['k0_B'] || 0.0;
    this.k0_C = params['k0_C'] || 0.0;
    this.kinf_A = params['kinf_A'] || 1.0;
    this.kinf_B = params['kinf_B'] || 0.0;
    this.kinf_C = params['kinf_C'] || 0.0;
    this.Fc = params['Fc'] || 0.6;
    this.N = params['N'] || 1.0;
    this.reactants = params['reactants'];
    this.products = params['products'];
    this.name = params['name'];
    this.gas_phase = params['gas_phase'];
    this.other_properties = {};
    Object.entries(params).forEach(([key, value]) => {
      if (this.#keys.includes(key) == false) {
        this.other_properties[key] = value;
      }
    });
  }
  getJSON() {
    let obj = {};
    obj['name'] = this.name;
    obj['type'] = 'TROE';
    obj['k0_A'] = this.k0_A;
    obj['k0_B'] = this.k0_B;
    obj['k0_C'] = this.k0_C;
    obj['kinf_A'] = this.kinf_A;
    obj['kinf_B'] = this.kinf_B;
    obj['kinf_C'] = this.kinf_C;
    obj['Fc'] = this.Fc;
    obj['N'] = this.N;
    obj['gas phase'] = this.gas_phase;
    obj['reactants'] = this.reactants.map((r) => r.getJSON());
    obj['products'] = this.products.map((p) => p.getJSON());
    const ops = convertOtherProperties(this.other_properties);
    Object.assign(obj, ops);
    return obj;
  }
}

class TernaryChemicalActivation {
  #keys = [
    'k0_A',
    'k0_B',
    'k0_C',
    'kinf_A',
    'kinf_B',
    'kinf_C',
    'Fc',
    'N',
    'reactants',
    'products',
    'name',
    'gas_phase',
  ];
  constructor(params) {
    this.k0_A = params['k0_A'] || 1.0;
    this.k0_B = params['k0_B'] || 0.0;
    this.k0_C = params['k0_C'] || 0.0;
    this.kinf_A = params['kinf_A'] || 1.0;
    this.kinf_B = params['kinf_B'] || 0.0;
    this.kinf_C = params['kinf_C'] || 0.0;
    this.Fc = params['Fc'] || 0.6;
    this.N = params['N'] || 1.0;
    this.reactants = params['reactants'];
    this.products = params['products'];
    this.name = params['name'];
    this.gas_phase = params['gas_phase'];
    this.other_properties = {};
    Object.entries(params).forEach(([key, value]) => {
      if (this.#keys.includes(key) == false) {
        this.other_properties[key] = value;
      }
    });
  }
  getJSON() {
    let obj = {};
    obj['type'] = 'TERNARY_CHEMICAL_ACTIVATION';
    obj['name'] = this.name;
    obj['gas phase'] = this.gas_phase;
    obj['k0_A'] = this.k0_A;
    obj['k0_B'] = this.k0_B;
    obj['k0_C'] = this.k0_C;
    obj['kinf_A'] = this.kinf_A;
    obj['kinf_B'] = this.kinf_B;
    obj['kinf_C'] = this.kinf_C;
    obj['Fc'] = this.Fc;
    obj['N'] = this.N;
    obj['reactants'] = this.reactants.map((r) => r.getJSON());
    obj['products'] = this.products.map((p) => p.getJSON());
    const ops = convertOtherProperties(this.other_properties);
    Object.assign(obj, ops);
    return obj;
  }
}

class Tunneling {
  #keys = ['A', 'B', 'C', 'reactants', 'products', 'name', 'gas_phase'];
  constructor(params) {
    this.A = params['A'] || 1.0;
    this.B = params['B'] || 0.0;
    this.C = params['C'] || 0.0;
    this.reactants = params['reactants'];
    this.products = params['products'];
    this.name = params['name'];
    this.gas_phase = params['gas_phase'];
    this.other_properties = {};
    Object.entries(params).forEach(([key, value]) => {
      if (this.#keys.includes(key) == false) {
        this.other_properties[key] = value;
      }
    });
  }
  getJSON() {
    let obj = {};
    obj['type'] = 'TUNNELING';
    obj['name'] = this.name;
    obj['A'] = this.A;
    obj['B'] = this.B;
    obj['C'] = this.C;
    obj['gas phase'] = this.gas_phase;
    obj['reactants'] = this.reactants.map((r) => r.getJSON());
    obj['products'] = this.products.map((p) => p.getJSON());
    const ops = convertOtherProperties(this.other_properties);
    Object.assign(obj, ops);
    return obj;
  }
}

class UserDefined {
  #keys = ['scaling_factor', 'reactants', 'products', 'name', 'gas_phase'];
  constructor(params) {
    this.scaling_factor = params['scaling_factor'] || 1.0;
    this.reactants = params['reactants'];
    this.products = params['products'];
    this.name = params['name'];
    this.gas_phase = params['gas_phase'];
    this.other_properties = {};
    Object.entries(params).forEach(([key, value]) => {
      if (this.#keys.includes(key) == false) {
        this.other_properties[key] = value;
      }
    });
  }
  getJSON() {
    let obj = {};
    obj['type'] = 'USER_DEFINED';
    obj['name'] = this.name;
    obj['scaling factor'] = this.scaling_factor;
    obj['gas phase'] = this.gas_phase;
    obj['reactants'] = this.reactants.map((r) => r.getJSON());
    obj['products'] = this.products.map((p) => p.getJSON());
    const ops = convertOtherProperties(this.other_properties);
    Object.assign(obj, ops);
    return obj;
  }
}

export const reactionTypes = {
  Arrhenius,
  Branched,
  Emission,
  FirstOrderLoss,
  Photolysis,
  Surface,
  TaylorSeries,
  Troe,
  TernaryChemicalActivation,
  Tunneling,
  UserDefined,
};
