use :node;

var Node = module.require('../Node').Node;

fn RangeMemberExpression(object, range)

extends Node {

this.type = 'RangeMemberExpression';
this.object = object;
this.object.parent = this;

this.range = range;
this.range.parent = this;

}

RangeMemberExpression.prototype.codegen = () -> {

if !super.codegen() {
  return;
}

var isNumber = (n) -> !::isNaN(::parseFloat(n)) && ::isFinite(n);

// If this node is the left side of an assignment expression,  
// it means we're dealing with splice. For example: 
// items[1..2] = [1, 2];
if this.parent.type == 'AssignmentExpression' &&
   this.parent.left == this {
  this.parent.type = 'CallExpression';

  this.parent.callee = {
    "type": "MemberExpression",
    "computed": false,
    "object": {
      "type": "MemberExpression",
      "computed": false,
      "object": {
        "type": "ArrayExpression",
        "elements": []
      },
      "property": {
        "type": "Identifier",
        "name": "splice"
      }
    },
    "property": {
      "type": "Identifier",
      "name": "apply"
    }
  };

  var to;
  var start = this.range.start.codegen() if this.range.start else {
               "type": "Literal",
               "value": 0
             };   

  if this.range.to? {
    if isNumber(start.value) &&
       isNumber(this.range.to.value) {
      to = {
        "type": "Literal",
        "value": this.range.to.value - start.value + (1 if this.range.operator == '..' else 0)
      };
    } else {
      to = this.range.to.codegen();

      if start.value != 0 {
        to = {
          "type": "BinaryExpression",
          "operator": "-",
          "left": to,
          "right": start,
        };
      }

      if this.range.operator == '..' {
        to = {
          "type": "BinaryExpression",
          "operator": "+",
          "left": to,
          "right": {
            "type": "Literal",
            "value": 1
          }
        };
      }
    }          
  } else {
    to = {
      "type": "Literal",
      "value": 9000000000,
      "raw": "9e9"
    };
  }  

  ::Object.defineProperty(this.parent, 'arguments', { 
    value: [
      this.object.codegen(),
      {
        "type": "CallExpression",
        "callee": {
          "type": "MemberExpression",
          "computed": false,
          "object": {
            "type": "ArrayExpression",
            "elements": [start, to]
          },
          "property": {
            "type": "Identifier",
            "name": "concat"
          }
        },
        "arguments": [
          this.parent.right
        ]
      }
    ], 
    enumerable: true 
  });
} else {
  // Otherwise, we're dealing with slice. For example:
  // var x = items[1..2];
  this.type = "CallExpression";
  this.callee = {
    "type": "MemberExpression",
    "computed": false,
    "object": this.object.codegen(),
    "property": {
      "type": "Identifier",
      "name": "slice"
    }
  };

  var args = [];

  if !this.range.start? {
    args.push({
      "type": "Literal",
      "value": 0
    });
  } else {
    args.push(this.range.start.codegen());
  }

  if this.range.to? {
    if this.range.operator == '...' {
      args.push(this.range.to.codegen());
    } else {
      if this.range.to.value && isNumber(this.range.to.value) {
        args.push({
          "type": "Literal",
          "value": this.range.to.value + 1
        });
      } else {
        args.push({
          "type": "BinaryExpression",
          "operator": "+",
          "left": this.range.to.codegen(),
          "right": {
            "type": "Literal",
            "value": 1
          }          
        });
      }
    }
  }

  ::Object.defineProperty(this, 'arguments', { 
    value: args, 
    enumerable: true 
  });
}

return this;

};

RangeMemberExpression.prototype.hasCallExpression = () -> true;

exports.RangeMemberExpression = RangeMemberExpression;