import BinaryOpPatcher from './BinaryOpPatcher.js';
import NodePatcher from './../../../patchers/NodePatcher.js';
import type { Node, ParseContext, Editor } from './../../../patchers/types.js';

/**
 * Handles logical AND and logical OR.
 *
 * This class is primarily responsible for rewriting `and` to `&&` and `or` to
 * `||`. It also applies De Morgan's laws [1] in the event of negation, such as
 * when used as the condition of an `unless` expression:
 *
 *   a unless b && c  # equivalent to `a if !b || !c`
 *
 * [1]: https://en.wikipedia.org/wiki/De_Morgan%27s_laws
 */
export default class LogicalOpPatcher extends BinaryOpPatcher {
  /**
   * `node` is expected to be either `LogicalAndOp` or `LogicalOrOp`.
   */
  constructor(node: Node, context: ParseContext, editor: Editor, left: NodePatcher, right: NodePatcher) {
    super(node, context, editor);
    this.left = left;
    this.right = right;
    this.logicToken = this.tokenBetweenPatchersMatching(this.left, this.right, 'LOGIC');
    this.replacement = this.logicToken.data;
  }

  /**
   * LEFT OP RIGHT
   */
  patchAsExpression() {
    let { left, right, replacement, logicToken } = this;
    left.patch();
    this.overwrite(...logicToken.range, replacement);
    right.patch();
  }

  patchAsStatement() {
    this.patchAsExpression();
  }

  /**
   * Apply De Morgan's law.
   */
  negate() {
    this.replacement = this.replacement === '&&' ? '||' : '&&';
    this.left.negate();
    this.right.negate();
  }
}

@@ -916,131 +916,23 @@
 his.
-logicToken = this.tokenBetweenPatchersMatching(this.left, this.right, 'LOGIC');%0A    this.replacement = this.logicToken.data
+negated = false
 ;%0A  
@@ -997,75 +997,72 @@
     
-let %7B left, right, replacement, logic
+this.left.patch();%0A    let operator
 Token
- %7D
  = this
-;%0A    left.patch
+.getOperatorToken
 ();%0A
@@ -1084,47 +1084,102 @@
 ite(
-...logicToken.range, replacement
+%0A      operatorToken.start,%0A      operatorToken.end,%0A      this.getOperator()%0A    
 );%0A    
+this.
 righ
@@ -1294,85 +1294,488 @@
    *
-/%0A  negate() %7B%0A    this.replacement = this.replacement === '&&' ? '%7C%7C' : '&&'
+%0A   * @private%0A   */%0A  getOperator(): string %7B%0A    let operatorToken = this.getOperatorToken();%0A    let operator = this.context.source.slice(%0A      operatorToken.start,%0A      operatorToken.end%0A    );%0A    if (operator === 'and') %7B%0A      operator = '&&';%0A    %7D else if (operator === 'or') %7B%0A      operator = '%7C%7C';%0A    %7D%0A    if (this.negated) %7B%0A      return operator === '&&' ? '%7C%7C' : '&&';%0A    %7D else %7B%0A      return operator;%0A    %7D%0A  %7D%0A%0A  negate() %7B%0A    this.negated = !this.negated
 ;%0A  

Use source tokens in `LogicalOpPatcher`.