/**
   * if any column is pressed, then we will create a filled box for the dragging the field box will
   * start from the y starting of the beginning point to the last y point the mouse is left
   *
   * @param currentX the current x of the drag
   * @param currentY the current y of the drag
   * @param oldX
   * @param oldY
   * @return
   */
  @Override
  public boolean mousedragged(int currentX, int currentY, int oldX, int oldY) {
    // checked if the beginning of the dragging is within a specific vertical field.
    // if it is, then we will create a filled box, from the y starting of the beginning point to the
    // last y point the mouse is left

    if (pressedColumn >= 0) { // check if pressing has began on a column

      setSelectedColumn(NONE_SELECTED); // unset the selected column

      // reset the previous drag
      verticalFieldsDragged[pressedColumn].setBeganDragging(true);
      resetPreviousColumnDrag();

      /* make sure the currentY does not go beyond the boundary of the line */
      if (currentY > (init_y + line_height)) { // if it is greater than the highest point for data
        currentY = init_y + line_height;
      } else if (currentY < init_y) { // if it is less than the lowest point for data
        currentY = init_y;
      }

      // set the height of the rectangle.
      // compute the current height of the column. Note it will be currentY and the beginning of the
      // dragging of the Y.
      int height = (currentY - verticalFieldsDragged[pressedColumn].getDragBeginY());

      if (height < 0) { // if drawing up
        height = Math.abs(height); // find the positive value of the height
        int newY = verticalFieldsDragged[pressedColumn].getDragBeginY() - height;
        // set the new Y
        verticalFieldsDragged[pressedColumn].setY(newY);
        // System.out.println("The height is " + height);
      }

      verticalFieldsDragged[pressedColumn].setH(height);

      verticalFieldsDragged[pressedColumn].setDragged(true);
      // remove the tooltip when dragging is on
      this.setToolTipText("");
      return true;
    }
    return false;
  }
  /**
   * if mouse was pressed on a column and began_dragging of the column is true then set the last
   * height of the dragging
   *
   * @param x x-coordinate of the mouse pointer
   * @param y y-coordinate of the mouse pointer
   * @param button
   * @return true or false
   */
  @Override
  public boolean mousereleased(int x, int y, int button) {
    // int index;
    // check if mouse was pressed on a column and began_dragging of the column is true
    if (pressedColumn >= 0) {
      if (verticalFieldsDragged[pressedColumn].getBeganDragging() == true) {
        /* make sure the y does not go beyond the boundary of the line */
        if (y > (init_y + line_height)) { // if it is greater than the highest point for data
          y = init_y + line_height;
        } else if (y < init_y) { // if it is less than the lowest point for data
          y = init_y;
        }

        int height = (y - verticalFieldsDragged[pressedColumn].getDragBeginY());

        if (height < 0) {
          height = Math.abs(height); // find the positive value of the height

          int newY = verticalFieldsDragged[pressedColumn].getDragBeginY() - height;
          // set the new Y
          verticalFieldsDragged[pressedColumn].setY(newY);
        }

        verticalFieldsDragged[pressedColumn].setH(height);

        verticalFieldsDragged[pressedColumn].setBeganDragging(false);

        verticalFieldsDragged[pressedColumn].setDragged(true);
        setPressedColumn(NONE_SELECTED); // reset the pressed column

        return true;
      }
    }

    return false;
  }
  /**
   * *********************************************************** Render the dataLines that connect
   * the vertical lines
   *
   * @param g - the graphics2D object **********************************************************
   */
  public void renderDataLines(Graphics2D g) {
    float curpoint, nextpoint;
    int x, y;
    /*Draw the lines of the data  */
    double curmax, nextmax;
    g.setStroke(new BasicStroke(0.2f));

    for (int i = 0; i < rowCount; i++) {
      x = init_x;
      y = init_y;

      for (int j = 0; j < colCount - 1; j++) {

        dataLinesColor = dataLinesColors[i];
        g.setColor(
            new Color(
                dataLinesColor.getRed(),
                dataLinesColor.getGreen(),
                dataLinesColor.getBlue(),
                dataLinesColor.getAlpha()));

        // g.setColor(dataLinesColors[i],alpha);
        // set the point on the current line
        curmax = colMax[j] - colMin[j];
        // to
        curpoint = (float) ((line_height / curmax) * XY_coords[i][j]);
        // set the point on the next line
        nextmax = colMax[j + 1] - colMin[j + 1];
        // set
        nextpoint = (float) ((line_height / nextmax) * XY_coords[i][j + 1]);
        //  }

        g.draw(
            new Line2D.Float(
                x,
                Math.abs((line_height + y) - curpoint),
                x + separation,
                Math.abs((line_height + y) - nextpoint)));
        // g.drawLine(x, Math.abs((y + curpoint) - line_height), x + separation, Math.abs((y +
        // nextpoint)-line_height));

        // Draw the small ovals for the data
        dataOvals[i][j] =
            new SmallOvals(
                x - SMALL_OVAL_SIZE / 2,
                (int) Math.abs((line_height + y) - curpoint - 1),
                SMALL_OVAL_SIZE,
                SMALL_OVAL_SIZE);
        dataOvals[i][j + 1] =
            new SmallOvals(
                x + separation - SMALL_OVAL_SIZE / 2,
                (int) Math.abs((line_height + y) - nextpoint - 1),
                SMALL_OVAL_SIZE,
                SMALL_OVAL_SIZE);

        // set the labels for the dataovals
        dataOvals[i][j].setLabel(data[i][j]);

        dataOvals[i][j + 1].setLabel(data[i][j + 1]);

        // distance between vertial lines
        x += separation;
      }
    }
  }
  /**
   * generates the color gradients to be used for the lines based on a selected column
   *
   * @param colid : the selected column id.
   */
  public void setLineColors() {

    int colid = selectedColumn; // the selected column. the initial value for selected column is 0

    if (colid >= 0) {
      /*varying the color of the lines */
      for (int i = 0; i < rowCount; i++) {
        // find the percentage value of the coordinate to be used as offset
        float offset = (colMax[colid] - XY_coords[i][colid]) / colMax[colid];

        float red, green, blue;

        red = dataLinesColor.getRed();
        green = dataLinesColor.getGreen();
        blue = dataLinesColor.getBlue();

        /*The color gradient combination will range between red and blue. Green will remain constant
         * NB:
         */
        dataLinesColors[i] =
            new Color(
                (int) Math.abs((red - (offset * MAX_RGB))),
                (int) Math.abs(((green))),
                (int) Math.abs((blue - (offset * MAX_RGB))),
                alpha);
      }
    } else { // check the dragged coordinates and draw those lines

      colid = pressedColumn; // the selected column. the initial value for selected column is 0

      // if no column is selected, we will select the first column by default for the data line
      // colouring

      if (colid < 0) { // do nothing

        return; // no need to do colors
      }

      /*varying the color of the lines */
      for (int i = 0; i < rowCount; i++) {
        float r, g, b;

        r = dataLinesColor.getRed();
        g = dataLinesColor.getGreen();
        b = dataLinesColor.getBlue();

        float offset = (colMax[colid] - XY_coords[i][colid]) / colMax[colid];

        if (dataWithinDragged(i, colid) == true) {
          // if ((curpoint >= dragY) && (curpoint <= (dragY + dragHeight))) {
          dataLinesColors[i] =
              new Color(
                  (int) Math.abs((r - (offset * MAX_RGB))),
                  (int) Math.abs(((g))),
                  (int) Math.abs((b - (offset * MAX_RGB))),
                  255); // note the alpha of this one will be opaque */

        } else {
          dataLinesColors[i] =
              new Color(
                  (int) Math.abs((r - (offset * MAX_RGB))),
                  (int) Math.abs(((g))),
                  (int) Math.abs((b - (offset * MAX_RGB))),
                  5); // note the alpha of this one will be close to transparent
        }
      }
    }
  }
  /**
   * ************************************************************************************ Initialize
   * the canvas, and render all the vertical lines of the parallel coordinates
   *
   * @param g - the graphic2D object
   *     ************************************************************************************
   */
  public void initializeCanvas(Graphics2D g) {

    // set the color gradients using the selected column default is the first column i.e. 0)
    this.setLineColors();

    int x = init_x, y = init_y, miny = init_miny, maxy = init_maxy;

    // set background of the canvas to white
    g.setBackground(new Color(255, 240, 240));

    g.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);

    g.setStroke(new BasicStroke(0.3f)); // set the thickness of the stroke
    // render all vertical lines
    for (int i = 0; i < colCount; i++) {
      g.setColor(vertLinesColor); // set the color
      // render vertical lines line_height pixels high
      g.drawLine(x, miny, x, maxy);
      // draw small horizontal lines to mark the dimensions of the data on the vertical lines
      g.drawLine(x - 3, y, x + 3, y);
      g.drawLine(x - 3, y + line_height, x + 3, y + line_height);

      // render 2 vertical lines on the sides of the main vertical lines to form boxes around them.
      g.setColor(new Color(170, 150, 140, 100));
      g.drawLine(x - 2, miny, x - 2, maxy);
      g.drawLine(x + 2, miny, x + 2, maxy);

      /** ************ Printing the Headings ************************ */
      // set color used to print the headings
      g.setColor(new Color(0, 0, 0));

      AffineTransform orig = g.getTransform(); // get the current transform

      double rotateRadian = 0, offset = 0, labelX, halfRotAngle = 0;

      rotateRadian = (headerAngle) * Math.PI / 180; // radians = degrees * pi/180
      halfRotAngle = (headerAngle / 2.0) * Math.PI / 180;
      // compute the offset
      offset =
          2
              * Math.sin((halfRotAngle))
              * (x - 10); // 2 * SOH to find the base of the isocelles triangle found

      labelX = (x - 10); // the 5 is to move the text a 5pixels to the left when it is rotated

      offset = Math.abs(offset);
      // rotateRadian = angle * Math.PI/180  ;  //radians = degrees * pi/180
      g.rotate(-rotateRadian);

      double xtrans = 0, ytrans = 0;
      double xrad = 0;
      /*find the distance of the x after the rotation. : i.e. find the distance where the base of the isoceles triangle
       *  formed during the rotation forms a right-angle with the header of the rotated figure
       */
      int isoAngle = (180 - headerAngle) / 2;
      xrad = (isoAngle) * Math.PI / 180;
      ytrans = Math.sin(xrad) * offset;

      xtrans = Math.sqrt(Math.pow(offset, 2) - Math.pow(ytrans, 2));
      labelX =
          (x - 10)
              - xtrans; // (x-5) because we want the label to start a few pixels before the vertical
                        // line

      g.drawString(
          tab.getColumnName(i),
          (int) labelX,
          (int)
              ((miny - 10)
                  + ytrans)); // write the header on top of the line. Note the 10 is because we
                              // wanted the text to be 30 pixels above the vertical line.
      // after writing the rotated text, continue with the normal rotation.
      g.setTransform(orig);

      /* Set the field dimensions. these dimensions represent the box around the vertical lines*/
      verticalFields[i] =
          new VerticalFields(
              x - (verticalFieldWidth / 2), y - 20, line_height + 40, verticalFieldWidth);

      /*Filling of selection*/
      if (verticalFieldsDragged[i]
          .getDragged()) { // if the column has been dragged, then fill the drag area
        verticalFieldsDragged[i].setX(verticalFields[i].getX());
        verticalFieldsDragged[i].setFill(true);
        verticalFieldsDragged[i].draw(g);
      } else if (i
          == selectedColumn) { // if the whole column has been selected then fill the whole column
        verticalFields[i].setFill(true);
        verticalFields[i].draw(g);
      }

      x += separation;
    }
    // render top and bottom ovals at the endpoints of the vertical lines
    x = init_x;

    for (int i = 0; i < colCount; i++) {
      bottomOvals[i] =
          new SmallOvals(x - OVAL_WIDTH / 2, y + line_height + 20, OVAL_WIDTH, OVAL_HEIGHT);
      topOvals[i] =
          new SmallOvals(x - OVAL_WIDTH / 2, y - 20 - OVAL_HEIGHT, OVAL_WIDTH, OVAL_HEIGHT);

      topOvals[i].setLabel(tab.getColumnName(i));
      bottomOvals[i].setLabel(tab.getColumnName(i));
      topOvals[i].draw(g);
      bottomOvals[i].draw(g);

      x += separation; // the horizontal separation of the vertical lines.
    }
  }