"use strict";

exports.__esModule = true;
exports.upsertAlignmentRun = exports.updateAlignmentViewVisibility = exports.removeAlignmentFromRedux = exports["default"] = exports.alignmentRunUpdate = void 0;
var _sequenceUtils = require("@teselagen/sequence-utils");
var _bioParsers = require("@teselagen/bio-parsers");
var _shortid = _interopRequireDefault(require("shortid"));
var _addDashesForMatchStartAndEndForTracks = _interopRequireDefault(require("./utils/addDashesForMatchStartAndEndForTracks"));
var _reduxAct = require("redux-act");
var _lodash = require("lodash");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
var alignmentAnnotationSettings = {
  axis: true,
  axisNumbers: true,
  chromatogram: false,
  dnaColors: false,
  features: false,
  parts: false,
  reverseSequence: false,
  sequence: true,
  translations: true,
  orfs: false,
  orfTranslations: false,
  cdsFeatureTranslations: false,
  cutsites: false,
  primers: false,
  compactNames: false
};
var defaultVisibilities = {
  alignmentAnnotationVisibility: alignmentAnnotationSettings,
  pairwise_alignmentAnnotationVisibility: alignmentAnnotationSettings,
  alignmentAnnotationLabelVisibility: {
    features: false,
    parts: false,
    cutsites: false
  },
  pairwise_alignmentAnnotationLabelVisibility: {
    features: false,
    parts: false,
    cutsites: false
  }
};
var defaultVisibilityTypes = Object.keys(defaultVisibilities);
try {
  defaultVisibilityTypes.forEach(function (type) {
    var newVal = JSON.parse(window.localStorage.getItem(type));
    if (newVal) defaultVisibilities[type] = _extends({}, defaultVisibilities[type], newVal);
  });
} catch (e) {
  console.error("error setting localstorage visibility config", e);
}

// ------------------------------------
// Actions
// ------------------------------------
var upsertAlignmentRun = (0, _reduxAct.createAction)("UPSERT_ALIGNMENT_RUN");
exports.upsertAlignmentRun = upsertAlignmentRun;
var removeAlignmentFromRedux = (0, _reduxAct.createAction)("REMOVE_ALIGNMENT_FROM_REDUX");
exports.removeAlignmentFromRedux = removeAlignmentFromRedux;
var updateAlignmentViewVisibility = (0, _reduxAct.createAction)("UPDATE_ALIGNMENT_VIEW_VISIBILITY");
exports.updateAlignmentViewVisibility = updateAlignmentViewVisibility;
var alignmentRunUpdate = (0, _reduxAct.createAction)("ALIGNMENT_RUN_UPDATE");
exports.alignmentRunUpdate = alignmentRunUpdate;
var highlightRangeProps = {
  color: "red",
  hideCarets: true,
  ignoreGaps: true
};
function addHighlightedDifferences(alignmentTracks) {
  return alignmentTracks.map(function (track) {
    if (track.isUnmapped) {
      return track;
    }
    var sequenceData = (0, _sequenceUtils.tidyUpSequenceData)(track.sequenceData);
    var matchHighlightRanges = getRangeMatchesBetweenTemplateAndNonTemplate(alignmentTracks[0].alignmentData.sequence, track.alignmentData.sequence);
    // .filter by the user-specified mismatch overrides (initially [])
    var mismatches = matchHighlightRanges.filter(function (_ref) {
      var isMatch = _ref.isMatch;
      return !isMatch;
    });
    return _extends({}, track, {
      sequenceData: sequenceData,
      matchHighlightRanges: matchHighlightRanges,
      additionalSelectionLayers: matchHighlightRanges.filter(function (_ref2) {
        var isMatch = _ref2.isMatch;
        return !isMatch;
      }).map(function (range) {
        return _extends({}, range, highlightRangeProps);
      }),
      mismatches: mismatches
    });
  });
}

// alignmentTracks = addHighlightedDifferences(alignmentTracks);

// ------------------------------------
// Reducer
// ------------------------------------
var _default = function _default(state, _ref3) {
  if (state === void 0) {
    state = {};
  }
  var _ref3$payload = _ref3.payload,
    payload = _ref3$payload === void 0 ? {} : _ref3$payload,
    type = _ref3.type;
  if (type === "ALIGNMENT_RUN_UPDATE") {
    var _extends2;
    var alignmentId = payload.alignmentId;
    var newState = _extends({}, state, (_extends2 = {}, _extends2[alignmentId] = _extends({}, state[alignmentId], payload), _extends2));
    return newState;
  }
  if (type === "UPDATE_ALIGNMENT_VIEW_VISIBILITY") {
    var _extends3;
    defaultVisibilityTypes.forEach(function (type) {
      if (type.startsWith("pairwise_") && payload.pairwiseAlignments || !type.startsWith("pairwise_") && !payload.pairwiseAlignments) {
        defaultVisibilities[type] = _extends({}, defaultVisibilities[type], payload[type.replace("pairwise_", "")]);
        localStorage.setItem(type, JSON.stringify(_extends({}, defaultVisibilities[type], payload[type.replace("pairwise_", "")])));
      }
    });
    return _extends({}, state, (_extends3 = {}, _extends3[payload.id] = _extends({}, payload), _extends3));
  }
  if (type === "UPSERT_ALIGNMENT_RUN") {
    var _state$id, _state$id2, _extends4;
    var id = payload.id;
    var payloadToUse = _extends({
      stateTrackingId: ((_state$id = state[id]) === null || _state$id === void 0 ? void 0 : _state$id.stateTrackingId) ? (0, _shortid["default"])() : "initialLoadId",
      alignmentType: (_state$id2 = state[id]) === null || _state$id2 === void 0 ? void 0 : _state$id2.alignmentType
    }, payload, defaultVisibilityTypes.reduce(function (acc, type) {
      if (type.startsWith("pairwise_") && payload.pairwiseAlignments || !type.startsWith("pairwise_") && !payload.pairwiseAlignments) {
        acc[type.replace("pairwise_", "")] = _extends({}, defaultVisibilities[type], payload[type.replace("pairwise_", "")]);
      }
      return acc;
    }, {}));
    if (payloadToUse.pairwiseAlignments) {
      if (payloadToUse.pairwiseAlignments[0][0].alignmentData.matchStart !== undefined) {
        payloadToUse.pairwiseAlignments = payloadToUse.pairwiseAlignments.map(_addDashesForMatchStartAndEndForTracks["default"]);
      }
      var templateSeq = payloadToUse.pairwiseAlignments[0][0];
      //we need to get all of the sequences in a single alignment (turning inserts into single BP red highlights)
      var pairwiseOverviewAlignmentTracks = [_extends({}, templateSeq, {
        sequenceData: (0, _sequenceUtils.tidyUpSequenceData)(templateSeq.sequenceData),
        alignmentData: {
          sequence: templateSeq.sequenceData.sequence
        } //remove the gaps from the template sequence
      })]; // start with just the template seq in there!

      payloadToUse.pairwiseAlignments.forEach(function (_ref4) {
        var template = _ref4[0],
          alignedSeq = _ref4[1];
        var condensedSeq = (0, _sequenceUtils.condensePairwiseAlignmentDifferences)(template.alignmentData.sequence, alignedSeq.alignmentData.sequence);
        var re = /r+/gi;
        var match;
        var additionalSelectionLayers = [];
        while ((match = re.exec(condensedSeq)) != null) {
          additionalSelectionLayers.push(_extends({
            start: match.index,
            end: match.index + match[0].length - 1
          }, highlightRangeProps));
        }
        re = /g+/gi;
        // let match;
        while ((match = re.exec(condensedSeq)) != null) {
          additionalSelectionLayers.push(_extends({
            start: match.index,
            end: match.index + match[0].length - 1
          }, highlightRangeProps, {
            color: "grey"
          }));
        }
        var alignedSeqMinusInserts = _extends({}, alignedSeq, {
          sequenceData: _extends({}, (0, _sequenceUtils.tidyUpSequenceData)(alignedSeq.sequenceData), {
            sequence: template.sequenceData.sequence
          }),
          additionalSelectionLayers: additionalSelectionLayers,
          alignmentData: {
            sequence: condensedSeq
          }
        });
        pairwiseOverviewAlignmentTracks.push(alignedSeqMinusInserts);
      });
      payloadToUse.pairwiseOverviewAlignmentTracks = pairwiseOverviewAlignmentTracks;
      payloadToUse.pairwiseAlignments = payloadToUse.pairwiseAlignments.map(addHighlightedDifferences);
    }
    if (payloadToUse.alignmentTracks) {
      payloadToUse.alignmentTracks = addHighlightedDifferences(payloadToUse.alignmentTracks);
    }
    //check for issues
    var hasError = checkForIssues(payloadToUse.alignmentTracks, payload.alignmentType);
    (payloadToUse.pairwiseAlignments || []).forEach(function (alignment) {
      var error = alignment;
      if (error) {
        hasError = error;
      }
    });
    return _extends({}, state, (_extends4 = {}, _extends4[payload.id] = _extends({}, payloadToUse, {
      hasError: hasError
    }), _extends4));
  }
  if (type === "REMOVE_ALIGNMENT_FROM_REDUX") {
    var _id = payload.id;
    state = (0, _lodash.omit)(state, [_id]);
  }
  return state;
}; //returns an array like so: [{start: 0, end: 4, isMatch: false}, {start,end,isMatch} ... etc]
exports["default"] = _default;
function getRangeMatchesBetweenTemplateAndNonTemplate(tempSeq, nonTempSeq) {
  //assume all sequences are the same length (with gap characters "-" in some places)
  //loop through all non template sequences and compare them with the template

  var seqLength = nonTempSeq.length;
  var ranges = [];
  // const startIndex = "".match/[-]/ Math.max(0, .indexOf("-"));
  var nonTempSeqWithoutLeadingDashes = nonTempSeq.replace(/^-+/g, "");
  var nonTempSeqWithoutTrailingDashes = nonTempSeq.replace(/-+$/g, "");
  var startIndex = seqLength - nonTempSeqWithoutLeadingDashes.length;
  var endIndex = seqLength - (seqLength - nonTempSeqWithoutTrailingDashes.length);
  for (var index = startIndex; index < endIndex; index++) {
    var isMatch = tempSeq[index].toLowerCase() === nonTempSeq[index].toLowerCase();
    var previousRange = ranges[ranges.length - 1];
    if (previousRange) {
      if (previousRange.isMatch === isMatch) {
        previousRange.end++;
      } else {
        ranges.push({
          start: index,
          end: index,
          isMatch: isMatch
        });
      }
    } else {
      ranges.push({
        start: startIndex,
        end: startIndex,
        isMatch: isMatch
      });
    }
  }
  return ranges;
}
function checkForIssues(alignmentTracks, alignmentType) {
  if (!alignmentTracks || !alignmentTracks[0] || !alignmentTracks[0].alignmentData) {
    return;
  }
  var alignmentTrackLength = alignmentTracks[0].alignmentData.sequence.length;
  var hasError;
  alignmentTracks.some(function (track) {
    if (track.alignmentData.sequence.length !== alignmentTrackLength) {
      console.error("incorrect length", alignmentTracks);
      return "incorrect length";
    }
    if (track.chromatogramData && track.sequenceData.sequence.length !== track.chromatogramData.baseCalls.length) {
      console.error("incorrect chromatogram length", alignmentTracks);
      return "incorrect chromatogram length";
    }
    if (track.chromatogramData && !track.chromatogramData.baseTraces) {
      if (!track.chromatogramData.basePos) {
        console.error("corrupted chromatogram data", alignmentTracks);
        return "corrupted chromatogram data";
      }
      track.chromatogramData = (0, _bioParsers.convertBasePosTraceToPerBpTrace)(track.chromatogramData);
    }
    if (alignmentType !== "Parallel Part Creation" && track.sequenceData.sequence.length !== track.alignmentData.sequence.replace(/-/g, "").length) {
      console.error("sequence data length does not match alignment data w/o gaps");
      console.error("track.sequenceData.sequence:", track.sequenceData.sequence);
      console.error("track.sequenceData.sequence.length:", track.sequenceData.sequence.length);
      console.error("track.alignmentData.sequence:", track.alignmentData.sequence);
      console.error('track.alignmentData.sequence.replace(/-/g,""):', track.alignmentData.sequence.replace(/-/g, ""));
      console.error('track.alignmentData.sequence.replace(/-/g,"").length:', track.alignmentData.sequence.replace(/-/g, "").length);
      hasError = "sequence data length does not match alignment data w/o gaps";
      return true;
    }
    return false;
  });
  if (hasError) {
    return hasError;
  }
}