// *****************************************************************************
// *****************************************************************************
// Copyright 2013 - 2016, Cadence Design Systems
// 
// This  file  is  part  of  the  Cadence  LEF/DEF  Open   Source
// Distribution,  Product Version 5.8. 
// 
// Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
// 
//        http://www.apache.org/licenses/LICENSE-2.0
// 
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
//    implied. See the License for the specific language governing
//    permissions and limitations under the License.
// 
// For updates, support, or to become part of the LEF/DEF Community,
// check www.openeda.org for details.
// 
//  $Author: dell $
//  $Revision: #1 $
//  $Date: 2017/06/06 $
//  $State:  $
// *****************************************************************************
// *****************************************************************************

#include <stdlib.h>
#include <string.h>
#include "defiPath.hpp"
#include "defiDebug.hpp"
#include "lex.h"
#include "defiUtil.hpp"

BEGIN_LEFDEF_PARSER_NAMESPACE

defiPath::defiPath(defrData *data)
: defData(data),
keys_(NULL),
data_(NULL),
pointer_(NULL),
numUsed_(0),
numAllocated_(0),
numX_(0),      
numY_(0),
stepX_(0),
stepY_(0),
deltaX_(0),
deltaY_(0),
mask_(0)
{}


defiPath::defiPath(defiPath *defiPathRef) 
{
    *this = *defiPathRef;

    defiPathRef->pointer_ = NULL;
    defiPathRef->keys_ = NULL;
    defiPathRef->data_ = NULL;    
}


defiPath::~defiPath() {
  Destroy();
}


void defiPath::Init() {
  // Should do nothing in constructor case. 
  Destroy();

  numUsed_ = 0;
  numAllocated_ = 0;
  pointer_ = new int;
  *pointer_ = -1;

  numX_ = 0;      
  numY_ = 0;
  stepX_ = 0;
  stepY_ = 0;
  deltaX_ = 0;
  deltaY_ = 0;
  mask_ = 0;

  bumpSize(16);
}


void defiPath::clear() {
  int i;

  for (i = 0; i < numUsed_; i++) {
    free(data_[i]);
    data_[i] = NULL;
  }

  numUsed_ = 0;

  delete pointer_;
  pointer_ = NULL;
}


void defiPath::Destroy() {

  if (keys_) {
    free((char*)(keys_));
    keys_ = NULL;
  }
  if (data_) {
     clear();
     free((char*)(data_));
     data_ = NULL;
  }
}


void defiPath::reverseOrder() {
  int one = 0;
  int two = numUsed_ - 1;
  int t;
  void *tptr;
  while (one < two) {
    t = keys_[one];
    keys_[one] = keys_[two];
    keys_[two] = t;
    tptr = data_[one];
    data_[one] = data_[two];
    data_[two] = tptr;
    one++;
    two--;
  }
}


void defiPath::initTraverse() const {
  *(pointer_) = -1;
}


void defiPath::initTraverseBackwards() const {
  *(pointer_) = numUsed_;
}


int 
defiPath::currentType() const
{
    if (*(pointer_) >= 0 && *(pointer_) < numUsed_) {
        switch (keys_[*(pointer_)]) {
            case 'L': return DEFIPATH_LAYER;
            case 'V': return DEFIPATH_VIA;
            case 'W': return DEFIPATH_WIDTH;
            case 'P': return DEFIPATH_POINT;
            case 'F': return DEFIPATH_FLUSHPOINT;
            case 'T': return DEFIPATH_TAPER;
            case 'R': return DEFIPATH_TAPERRULE;
            case 'S': return DEFIPATH_SHAPE;
            case 'Y': return DEFIPATH_STYLE;
            case 'O': return DEFIPATH_VIAROTATION;
            case 'E': return DEFIPATH_RECT;
            case 'D': return DEFIPATH_VIADATA;
            case 'U': return DEFIPATH_VIRTUALPOINT;
            case 'M': return DEFIPATH_MASK;
            case 'C': return DEFIPATH_VIAMASK;
            default : return DEFIPATH_DONE;
        } 
    }

    return DEFIPATH_DONE;
}

int defiPath::next() const 
{
    (*(pointer_))++;

    return currentType();
}


int defiPath::prev()  const{
  (*(pointer_))--;

  return currentType();
}

int defiPath::getTaper() const {
  if (keys_[*(pointer_)] != 'T') return 0;
  return 1;
}

const char* defiPath::getTaperRule() const {
  if (keys_[*(pointer_)] != 'R') return 0;
  return (char*)(data_[*(pointer_)]);
}

const char* defiPath::getLayer() const {
  if (keys_[*(pointer_)] != 'L') return 0;
  return (char*)(data_[*(pointer_)]);
}


const char* defiPath::getVia() const {
  if (keys_[*(pointer_)] != 'V') return 0;
  return (char*)(data_[*(pointer_)]);
}


const char* defiPath::getShape() const {
  if (keys_[*(pointer_)] != 'S') return 0;
  return (char*)(data_[*(pointer_)]);
}


int defiPath::getStyle() const {
  int *style;
  if (keys_[*(pointer_)] != 'Y') return 0;
  style = (int*)(data_[*(pointer_)]);
  return *style;
}


int defiPath::getWidth() const {
  int *wptr;
  if (keys_[*(pointer_)] != 'W') return 0;
  wptr = (int*)(data_[*(pointer_)]);
  return *wptr;
}

int defiPath::getViaRotation() const {
  int *wptr;
  if (keys_[*(pointer_)] != 'O') return 0;
  wptr = (int*)(data_[*(pointer_)]);
  return *wptr;
}

int defiPath::getMask() const {
    int *wptr;
    if (keys_[*(pointer_)] != 'M') return 0;
    wptr = (int*)(data_[*(pointer_)]);
    return *wptr;
}

int defiPath::getViaBottomMask() const {
    int *wptr;
    if (keys_[*(pointer_)] != 'C') return 0;
    wptr = (int*)(data_[*(pointer_)]);

    int viaMask = *wptr;

    return viaMask % 10;
}

int defiPath::getViaCutMask() const {
    int *wptr;
    if (keys_[*(pointer_)] != 'C') return 0;
    wptr = (int*)(data_[*(pointer_)]);

    int viaMask = *wptr;

    return viaMask / 10 % 10;
}

int defiPath::getViaTopMask() const {
    int *wptr;
    if (keys_[*(pointer_)] != 'C') return 0;
    wptr = (int*)(data_[*(pointer_)]);

    int viaMask = *wptr;

    return viaMask / 100;
}

const char* defiPath::getViaRotationStr() const {
  int *wptr;
  if (keys_[*(pointer_)] != 'O') return 0;
  wptr = (int*)(data_[*(pointer_)]);
  return defiOrientStr(*wptr);
}

void defiPath::getViaRect(int* deltaX1, int* deltaY1, int* deltaX2, int* deltaY2) const {
    if (keys_[*(pointer_)] != 'E') return ;
    *deltaX1 = ((struct defiViaRect*)(data_[*(pointer_)]))->deltaX1;
    *deltaY1 = ((struct defiViaRect*)(data_[*(pointer_)]))->deltaY1;
    *deltaX2 = ((struct defiViaRect*)(data_[*(pointer_)]))->deltaX2;
    *deltaY2 = ((struct defiViaRect*)(data_[*(pointer_)]))->deltaY2;
}

void defiPath::getViaData(int* numX, int* numY, int* stepX, int* stepY) const {
  if (keys_[*(pointer_)] != 'D') return ;
  *numX = ((struct defiViaData*)(data_[*(pointer_)]))->numX;
  *numY = ((struct defiViaData*)(data_[*(pointer_)]))->numY;
  *stepX = ((struct defiViaData*)(data_[*(pointer_)]))->stepX;
  *stepY = ((struct defiViaData*)(data_[*(pointer_)]))->stepY;
}


void defiPath::getFlushPoint(int* x, int* y, int* ext) const {
  if (keys_[*(pointer_)] != 'F') return ;
  *x = ((struct defiPnt*)(data_[*(pointer_)]))->x;
  *y = ((struct defiPnt*)(data_[*(pointer_)]))->y;
  *ext = ((struct defiPnt*)(data_[*(pointer_)]))->ext;
}

void defiPath::getVirtualPoint(int* x, int* y) const {
    if (keys_[*(pointer_)] != 'U') return ;
    *x = ((struct defiPnt*)(data_[*(pointer_)]))->x;
    *y = ((struct defiPnt*)(data_[*(pointer_)]))->y;
}

void defiPath::getPoint(int* x, int* y) const {
  if (keys_[*(pointer_)] != 'P') return ;
  *x = ((struct defiPnt*)(data_[*(pointer_)]))->x;
  *y = ((struct defiPnt*)(data_[*(pointer_)]))->y;
}


void defiPath::addWidth(int w) {
  int * wValue;
  if (numUsed_ == numAllocated_)
    bumpSize(numAllocated_ * 2);
  wValue = (int*)malloc(sizeof(int));
  *wValue = w;
  keys_[numUsed_] = 'W';
  data_[numUsed_] = wValue;
  (numUsed_)++;
}


void defiPath::addVia(const char* l) {
  int len = strlen(l)+1;
  if (numUsed_ == numAllocated_)
    bumpSize(numAllocated_ * 2);
  keys_[numUsed_] = 'V';
  data_[numUsed_] = malloc(len);
  strcpy((char*)(data_[numUsed_]), defData->DEFCASE(l));
  (numUsed_)++;
}


void defiPath::addViaRotation(int o) {
  int * orient;
  if (numUsed_ == numAllocated_)
    bumpSize(numAllocated_ * 2);
  orient = (int*)malloc(sizeof(int));
  *orient = o;
  keys_[numUsed_] = 'O';
  data_[numUsed_] = orient;
  (numUsed_)++;
}


void defiPath::addViaRect(int deltaX1, int deltaY1, int deltaX2, int deltaY2) {
    if (numUsed_ == numAllocated_)
        bumpSize(numAllocated_ * 2);
    keys_[numUsed_] = 'E';  // RECT
    data_[numUsed_] = malloc(sizeof(struct defiViaRect));
    ((struct defiViaRect*)(data_[numUsed_]))->deltaX1 = deltaX1;
    ((struct defiViaRect*)(data_[numUsed_]))->deltaY1 = deltaY1;
    ((struct defiViaRect*)(data_[numUsed_]))->deltaX2 = deltaX2;
    ((struct defiViaRect*)(data_[numUsed_]))->deltaY2 = deltaY2;
    (numUsed_)++;
}


void defiPath::addViaData(int numX, int numY, int stepX, int stepY) {
  if (numUsed_ == numAllocated_)
    bumpSize(numAllocated_ * 2);
  keys_[numUsed_] = 'D';
  data_[numUsed_] = malloc(sizeof(struct defiViaData));
  ((struct defiViaData*)(data_[numUsed_]))->numX = numX;
  ((struct defiViaData*)(data_[numUsed_]))->numY = numY;
  ((struct defiViaData*)(data_[numUsed_]))->stepX = stepX;
  ((struct defiViaData*)(data_[numUsed_]))->stepY = stepY;
  (numUsed_)++;
}


void defiPath::addLayer(const char* l) {
  int len = strlen(l)+1;
  if (numUsed_ == numAllocated_)
    bumpSize(numAllocated_ * 2);
  keys_[numUsed_] = 'L' ;
  data_[numUsed_] = malloc(len);
  strcpy((char*)(data_[numUsed_]), defData->DEFCASE(l));
  (numUsed_)++;
}


void defiPath::addTaperRule(const char* l) {
  int len = strlen(l)+1;
  if (numUsed_ == numAllocated_)
    bumpSize(numAllocated_ * 2);
  keys_[numUsed_] = 'R' ;
  data_[numUsed_] = malloc(len);
  strcpy((char*)(data_[numUsed_]), defData->DEFCASE(l));
  (numUsed_)++;
}


void defiPath::addPoint(int x, int y) {
  if (numUsed_ == numAllocated_)
    bumpSize(numAllocated_ * 2);
  keys_[numUsed_] = 'P';
  data_[numUsed_] = malloc(sizeof(struct defiPnt));
  ((struct defiPnt*)(data_[numUsed_])) -> x = x;
  ((struct defiPnt*)(data_[numUsed_])) -> y = y;
  (numUsed_)++;
}

void defiPath::addMask(int colorMask) {
    int* mask;
    if (numUsed_ == numAllocated_)
        bumpSize(numAllocated_ * 2);
    mask = (int*)malloc(sizeof(int));
    *mask = colorMask;
    keys_[numUsed_] = 'M';  //Mask for points
    data_[numUsed_] = mask;
    (numUsed_)++;
}

void defiPath::addViaMask(int colorMask) {
    int* mask;
    if (numUsed_ == numAllocated_)
        bumpSize(numAllocated_ * 2);
    mask = (int*)malloc(sizeof(int));
    *mask = colorMask;
    keys_[numUsed_] = 'C';  //viaMask
    data_[numUsed_] = mask;
    (numUsed_)++;
}

void defiPath::addFlushPoint(int x, int y, int ext) {
  if (numUsed_ == numAllocated_)
    bumpSize(numAllocated_ * 2);
  keys_[numUsed_] = 'F';
  data_[numUsed_] = malloc(sizeof(struct defiPnt));
  ((struct defiPnt*)(data_[numUsed_])) -> x = x;
  ((struct defiPnt*)(data_[numUsed_])) -> y = y;
  ((struct defiPnt*)(data_[numUsed_])) -> ext = ext;
  (numUsed_)++;
}

void defiPath::addVirtualPoint(int x, int y) {
    if (numUsed_ == numAllocated_)
        bumpSize(numAllocated_ * 2);
    keys_[numUsed_] = 'U';
    data_[numUsed_] = malloc(sizeof(struct defiPnt));
    ((struct defiPnt*)(data_[numUsed_])) -> x = x;
    ((struct defiPnt*)(data_[numUsed_])) -> y = y;
    (numUsed_)++;
}

void defiPath::setTaper() {
  if (numUsed_ == numAllocated_)
    bumpSize(numAllocated_ * 2);
  keys_[numUsed_] = 'T';
  data_[numUsed_] = 0;
  (numUsed_)++;
}


void defiPath::addShape(const char* l) {
  int len = strlen(l)+1;
  if (numUsed_ == numAllocated_)
    bumpSize(numAllocated_ * 2);
  keys_[numUsed_] = 'S' ;
  data_[numUsed_] = malloc(len);
  strcpy((char*)(data_[numUsed_]), defData->DEFCASE(l));
  (numUsed_)++;
}

void defiPath::addStyle(int s) {
  int *style;
  if (numUsed_ == numAllocated_)
    bumpSize(numAllocated_ * 2);
  style = (int*)malloc(sizeof(int));
  *style = s;
  keys_[numUsed_] = 'Y' ;
  data_[numUsed_] = style;
  (numUsed_)++;
}


void defiPath::print(FILE* fout) const {
  int i;
  int *wptr;
  if (fout == 0) fout = stdout;
  fprintf(fout, "Path:\n");
  for (i = 0; i < numUsed_; i++) {
    if (keys_[i] == 'L') {
        fprintf(fout, " layer %s\n", (char*)(data_[i]));
    } else if (keys_[i] == 'R') {
        fprintf(fout, " taperrule %s\n", data_[i] ? (char*)(data_[i]) : "" );
    } else if (keys_[i] == 'T') {
        fprintf(fout, " taper %s\n", data_[i] ? (char*)(data_[i]) : "" );
    } else if (keys_[i] == 'S') {
        fprintf(fout, " shape %s\n", data_[i] ? (char*)(data_[i]) : "" );
    } else if (keys_[i] == 'V') {
        fprintf(fout, " via %s\n", data_[i] ? (char*)(data_[i]) : "" );
    } else if (keys_[i] == 'O') {
        fprintf(fout, " via rotation %s\n",
                data_[i] ? (char*)(data_[i]) : "" );
    } else if (keys_[i] == 'M') {
        fprintf(fout, " mask %d\n", getMask() );
    } else if (keys_[i] == 'E') {
        fprintf(fout, " rect %d,%d,%d,%d\n",
                ((struct defiViaRect*)(data_[i]))->deltaX1,
                ((struct defiViaRect*)(data_[i]))->deltaY1,
                ((struct defiViaRect*)(data_[i]))->deltaX2,
                ((struct defiViaRect*)(data_[i]))->deltaY2);
    } else if (keys_[i] == 'W') {
        wptr = (int*)(data_[i]);
        fprintf(fout, " width %d\n", *wptr);
    } else if (keys_[i] == 'P') {
        fprintf(fout, " point %d,%d\n",
                ((struct defiPnt*)(data_[i]))->x,
                ((struct defiPnt*)(data_[i]))->y);
    } else if (keys_[i] == 'F') {
        fprintf(fout, " flushpoint %d,%d,%d\n",
                ((struct defiPnt*)(data_[i]))->x,
                ((struct defiPnt*)(data_[i]))->y,
                ((struct defiPnt*)(data_[i]))->ext);
    } else if (keys_[i] == 'U') {
        fprintf(fout, " virtualpoint %d,%d\n",
            ((struct defiPnt*)(data_[i]))->x,
            ((struct defiPnt*)(data_[i]))->y);
    } else if (keys_[i] == 'D') {
      fprintf(fout, " DO %d BY %d STEP %d %d\n",
              ((struct defiViaData*)(data_[i]))->numX,
              ((struct defiViaData*)(data_[i]))->numY,
              ((struct defiViaData*)(data_[i]))->stepX,
              ((struct defiViaData*)(data_[i]))->stepY);
    } else {
      fprintf(fout, " ERROR\n");
    }
  }
}


void defiPath::bumpSize(int size) {
  int i;
  int* newKeys = (int*)malloc(size * sizeof(int*));
  void** newData = (void**)malloc(size * sizeof(void*));

  for (i = 0; i < numUsed_; i++) {
    newKeys[i] = keys_[i];
    newData[i] = data_[i];
  }

  if (keys_) free((char*)(keys_));
  if (data_) free((char*)(data_));

  keys_ = newKeys;
  data_ = newData;
  numAllocated_ = size;
}


END_LEFDEF_PARSER_NAMESPACE

