import Acid from "./acid";
import { color } from "d3-color";
import Selection from "./selection"
export default class Protein {
  static scaffoldColor = "#92221f";
  static scaffoldHighlightColor = "#EF6565";
  static insertColor = "#1f7794";
  static insertHighlightColor = "#1fAAE4"

  constructor(name, pdb, dssp, chain, role, whatever_chain = false) {
    this.name = name;
    this.pdb = pdb;
    this.role = role;

    this.dsspAcids = [];
    this.customAcids = [];
    this.acidSource = "";

    this.chain = chain;

    this.customLoops = [];
    this.archDBLoops = [];
    this.computedLoops = [];
    this.computedSuperloops = [];

    this.availableBfactors = [];
    this.availableCcors = [];
    this.bfactorCorrelation;
    this.pdb_numbering_length;

    this.selection = [];
    // oooooh no you don't start your indexes at rundom numbers

    let first_index = null;
    for (let [index, acid] of dssp.entries()) {
      if (whatever_chain || (this.chain.toLowerCase() == acid.chain.toLowerCase())) {
        if (first_index == null) {
          first_index = index
        }
        this.dsspAcids.push(new Acid(
          Number(index - first_index), // normalize to start at zero
          whatever_chain ? "A" : acid.chain,
          acid.pdb_number,
          acid.secondary_structure,
          acid.acid,
        ))
      }
    }

    this.acidSource = "DSSP"

  }

  get color() {
    if (this.role == "scaffold") {
      return Protein.scaffoldColor;
    } else {
      return Protein.insertColor;
    }
  }

  get highlightColor() {
    if (this.role == "scaffold") {
      return Protein.scaffoldHighlightColor;
    } else {
      return Protein.insertHighlightColor;
    }
  }

  get displayName() {

    // censorship
    // if (this.role == "scaffold") {
    //   return "hldS";
    // } else {
    //   return "lucI";
    // }
    return this.name;
  }

  get loops() {
    let selectedLoops = this.customLoops.slice(0);
    if (this.hasComputedLoops) {
      selectedLoops = selectedLoops.concat(this.computedLoops);
    } else if (this.hasArchDBLoops) {
      selectedLoops = selectedLoops.concat(this.archDBLoops);
    }
    return selectedLoops;
  }
  get allLoops() {
    let selectedLoops = this.customLoops;
    selectedLoops = selectedLoops.concat(this.computedLoops);
    selectedLoops = selectedLoops.concat(this.archDBLoops);
    return selectedLoops;
  }
  get hasArchDBLoops() { return this.archDBLoops.length > 0 }
  get hasComputedLoops() { return this.computedLoops.length > 0 }
  get acids() { return this.dsspAcids }

  get minMaxPdbNumber() {
    let min = this.acids[0].pdb_number;
    let max = this.acids[0].pdb_number;
    for (let acid of this.acids) {
      min = Math.min(acid.pdb_number, min);
      max = Math.max(acid.pdb_number, max);
    }
    return {
      min, max
    }
  }

  get absolute_loop_maximum() {
    let values = this.loops.map((l) => l.flexibility);
    return this._findMaximumAbsoluteBfactor(values, this.availableBfactors);
  }

  get absolute_acid_maximum() {
    let values = this.acids.map((a) => a.bfactors.residue);
    return this._findMaximumAbsoluteBfactor(values, this.availableBfactors);
  }

  get absolute_ss_maximum() {
    let values = this.acids.map((a) => a.bfactors.secondary_structure);
    return this._findMaximumAbsoluteBfactor(values, this.availableBfactors);
  }

  getBestLoop(from, to) {
    let bestFit = null;
    for (let loop of this.superloops.concat(this.loops)) {
      if (from.pdb_number <= loop.start && loop.end <= to.pdb_number) { //is in requested range
        if (bestFit == null || loop.length > bestFit.length) { // is better fit 
          bestFit = loop;
        }
      }
    }
    return bestFit;
  }

  get representation() {
    return this.displayName + ':' + this.chain;
  }

  getAcid(pdbNumber) {
    return this.acids.find(a => a.pdb_number == pdbNumber);
  }

  getAcidByIndex(index) {
    return this.acids.find(a => a.index == index);
  }

  getAcids(from, to) {
    return this.acids.filter(
      a => from <= a.pdb_number && a.pdb_number <= to
    );
  }

  invalidateLoopsGeometry(from, to) {
    for (let loop of this.loops) {
      if ((from <= loop.end) && (to >= loop.start)) {
        loop.invalidateGeometry();
      }
    }
  }

  incorrectLoop(loop) {
    return this.getAcid(loop.start).simple_secondary_structure != "C" && this.getAcid(loop.end).simple_secondary_structure != "C";
  }


  containsIncorrectLoops() {
    for (let loop of this.loops) {
      if (!this.incorrectLoop(loop)) {
        return true;
      }
    }
    return false;
  }

  getSecsSequence() {
    return this.getAcids(Number.NEGATIVE_INFINITY, Number.POSITIVE_INFINITY).map(a => a.simple_secondary_structure);
  }

  _findMaximumAbsoluteBfactor(bfactorList, types) {
    let maximum = 0;
    for (let bfactor of bfactorList) {
      for (let type of types) {
        if (maximum < Math.abs(bfactor[type])) {
          maximum = Math.abs(bfactor[type]);
        }
      }
    }
    return maximum;
  }

}