import Drawable;
import Vertex2D;
import PlaneEqn;

class Triangle implements Drawable {
    protected Vertex2D v[];
    private PlaneEqn pe;

    float xMin, y_at_xMin, xMid, y_at_xMid, xMax, y_at_xMax;
    float yMin, x_at_yMin, yMid, x_at_yMid, yMax, x_at_yMax;
    float inactive_m, inactive_b;
    
    int breakpoint_type; // 0=upper-parallel, 1=lower-parallel, 2=left, 3=right

    public Triangle() {
    }

    public Triangle(Vertex2D v0, Vertex2D v1, Vertex2D v2) {

        // Create Plane equation from these three vertices
        pe = new PlaneEqn(v0, v1, v2);
        
        v = new Vertex2D[3];
        v[0] = v0;
        v[1] = v1;
        v[2] = v2;
        
        int t0 = v[0].argb;
        int t1 = v[1].argb;
        int t2 = v[2].argb;
        
        int i, j;
        
        Vertex2D key = new Vertex2D(v[0].x,v[0].y,v[0].argb);
        
        // Insertion sort the x vertices
        for (j = 2; j <= 3; j++)
        {
            key = v[j-1];
            for (i = j - 1; ( (i > 0) && (v[i-1].x > key.x) ); i--)
            {
                v[i+1-1] = v[i-1];
            }
            v[i+1-1] = key;
        }
        
        xMin = v[0].x; y_at_xMin = v[0].y;
        xMid = v[1].x; y_at_xMid = v[1].y;
        xMax = v[2].x; y_at_xMax = v[2].y;

        // Unambiguate x values
        float tempInt;
        if ( (xMid == xMin) && (y_at_xMid < y_at_xMin) )
        {
            tempInt = y_at_xMid;
            y_at_xMid = y_at_xMin;
            y_at_xMin = tempInt;
        }
        if ( (xMid == xMax) && (y_at_xMax < y_at_xMid) )
        {
            tempInt = y_at_xMax;
            y_at_xMax = y_at_xMid;
            y_at_xMax = tempInt;
        }

        // Insertion sort the y vertices
        for (j = 2; j <= 3; j++)
        {
            key = v[j-1];
            for (i = j - 1; ( (i > 0) && (v[i-1].y > key.y) ); i--)
            {
                v[i+1-1] = v[i-1];
            }
            v[i+1-1] = key;
        }
        
        yMin = v[0].y; x_at_yMin = v[0].x;
        yMid = v[1].y; x_at_yMid = v[1].x;
        yMax = v[2].y; x_at_yMax = v[2].x;
        
        // Unambiguate x values
        if ( (yMid == yMin) && (x_at_yMid < x_at_yMin) )
        {
            tempInt = x_at_yMid;
            x_at_yMid = x_at_yMin;
            x_at_yMin = tempInt;
        }
        if ( (yMid == yMax) && (x_at_yMax < x_at_yMid) )
        {
            tempInt = x_at_yMax;
            x_at_yMax = x_at_yMid;
            x_at_yMax = tempInt;
        }

        inactive_m = (yMin - yMax)/(x_at_yMin - x_at_yMax);
        inactive_b = yMin - (inactive_m * x_at_yMin);

        // Determine if the breakpoint lies on the left or right side of the polygon
        // We know that the break point will be at the middle y value
        if (yMid == yMin) // upper-parallel
            breakpoint_type = 0;
        else if (yMid == yMax) // lower-parallel
            breakpoint_type = 1;
        else // left or right, left if y(xMax) == yMax, right otherwise
        {   
            if (x_at_yMax > x_at_yMid)
                breakpoint_type = 2; // left breakpoint
            else
                breakpoint_type = 3; // right breakpoint
                
            if ( (x_at_yMin != x_at_yMax) &&
                 ( ((x_at_yMin == xMin) && (x_at_yMax == xMax)) ||
                   ((x_at_yMax == xMin) && (x_at_yMin == xMax)) // "bikini"
                 )
               )
            {
                if ( ((yMid - inactive_b)/inactive_m) > x_at_yMid )
                    breakpoint_type = 2; // left breakpoint
                else
                    breakpoint_type = 3; // right breakpoint
            }
        }
    }

    public void Draw(Raster r) {

        boolean drawn = false; //boolean
        
        float active_m, active_b;
        int x, y, active_x, inactive_x;

        if (breakpoint_type == 0) // upper parallel, yMid = yMin
        {
            active_m = (yMid - yMax)/(x_at_yMid - x_at_yMax);
            active_b = yMid - (active_m * x_at_yMid);
        }
        else if (breakpoint_type == 1) // lower parallel, yMid = yMax
        {
            active_m = (yMin - yMid)/(x_at_yMin - x_at_yMid);
            active_b = yMid - (active_m * x_at_yMid);
        }
        else
        {
            active_m = (yMin - yMid)/(x_at_yMin - x_at_yMid);
            active_b = yMid - (active_m * x_at_yMid);
        }

        // Draws for types 1, 2, 3 (upper, left, right)
        for (y = (int) (yMin + 0.5); y <= yMid; y++) // scan top->down
        {
            if ( (breakpoint_type == 1) ||  // lower-parallel
                 (breakpoint_type == 2) )   // left breakpoint
            {
                active_x   = 1 + (int) (((y -   active_b)/  active_m)); // use ceiling for leftmost pixel
                inactive_x = 0 + (int) (((y - inactive_b)/inactive_m)); // use floor for rightmost pixel
                if (xMid == xMax) // Check for inifinite slope
                    inactive_x = 0 + (int) (xMid);
                for (x = active_x; x <= inactive_x; x++) // scan left->right
                {
                    if (x > xMax) break;
                    if (x >= xMin)
                    {
                        r.setPixel(pe.get_argb(x, y), x, y);
                    }
                }
            }
            else if (breakpoint_type == 3) // right breakpoint
            {
                inactive_x = 1 + (int) (((y - inactive_b)/inactive_m)); // use ceiling for leftmost pixel
                if (xMid == xMin) // Check for inifinite slope
                    inactive_x = 1 + (int) (xMid);
                active_x   = 0 + (int) ((y -   active_b)/  active_m); // use floor for righmost pixel
                for (x = inactive_x; x <= active_x; x++) // scan left->right
                {
                    if (x > xMax) break;
                    if (x >= xMin)
                    {
                        r.setPixel(pe.get_argb(x, y), x, y);
                    }
                }
            }
        }

        active_m = (yMid - yMax)/(x_at_yMid - x_at_yMax);
        active_b = yMid - (active_m * x_at_yMid);

        // Draws for types 0, 2, 3 (lower, left, right)
        for (y = (int) (yMid + 0.5); y <= yMax; y++) // scan top->down
        {
            if (breakpoint_type == 2) // left breakpoint
            {
                active_x   = 1 + (int) ((y -   active_b)/  active_m); // use ceiling for leftmost pixel
                inactive_x = 0 + (int) ((y - inactive_b)/inactive_m); // use floor for rightmost pixel
                if (xMid == xMax) // Check for inifinite slope
                    inactive_x = 0 + (int) xMid;
                for (x = active_x; x <= inactive_x; x++) // scan left->right
                {
                    if (x > xMax) break;
                    if (x >= xMin)
                    {
                        r.setPixel(pe.get_argb(x, y), x, y);
                    }
                }
            }
            else if ( (breakpoint_type == 0) || // upper-parallel
                      (breakpoint_type == 3) )  // right breakpoint
            {
                inactive_x = 1 + (int) ((y - inactive_b)/inactive_m); // use ceiling for leftmost pixel
                if (xMid == xMin) // Check for inifinite slope
                    inactive_x = 1 + (int) xMid;
                active_x = 0 + (int) ((y -   active_b)/  active_m); // use floor for rightmost pixel
                if (x_at_yMid == x_at_yMax)
                    active_x = 0 + (int) x_at_yMax;
                for (x = inactive_x; x <= active_x; x++) // scan left->right
                {
                    if (x > xMax) break;
                    if (x >= xMin)
                    {
                        r.setPixel(pe.get_argb(x, y), x, y);
                    }
                }
            }
        }
    }
}

