#include <InterViews\X11\palette.h>
#include <Interviews\X11\dash.h>
#include <math.h>

const int Bit_1 = 0x0001;
const double Pix2 = 6.283185;

int GetSteps (int r) {
    if (r < 20) {
	return r;
    } else {
	return ((r*3) >> 2);
    }
}

Dash::Dash () {
    pattern = nil;
    count = 0;
}

Dash::~Dash () {
}

void Dash::SetPattern (Brush* b) {
    pattern = b->rep->info;
    count = b->rep->count;
}

void Dash::_MoveTo (Coord x, Coord y) {
    x0 = x; y0 = y;
}

void Dash::Init (Canvas* c, PainterRep* r) {
    canvas = c;
    rep = r;
    if (rep->foreground != nil) {
	foreground = PALETTEINDEX(rep->foreground->PixelValue());
    } else {
	foreground = RGB(0, 0, 0);
    }
    if (rep->background != nil) {
	background = PALETTEINDEX(rep->background->PixelValue());
    } else {
	background = RGB(255, 255, 255);
    }
    set = pattern[0];
    index = 0;
}

void Dash::_Init() {
}

void Dash::_Finish () {
}

void Dash::PutPixel () {
}

void Dash::_LineTo(Coord x1, Coord y1) {
    int  c1, c2, error;
    int  m, n, dx, dy;

    if ((x0 == x1) && (y0 == y1)) {
	PutPixel();
	return;
    }
    x = x0; y = y0;
    dx = x1 - x0;
    dy = y1 - y0;
    if (dx >= 0) {
	m = 1;
    } else {
	m = -1;
	dx = -dx;
    }
    if (dy >= 0) {
	n = 1;
    } else {
	n = -1;
	dy = -dy;
    }
    if (dx > dy) {
	error = (dy << 1) - dx;
	c1 = dy << 1;
	c2 = (dy << 1) - (dx << 1);
    } else {
	error = (dx << 1) - dy;
	c1 = dx << 1;
	c2 = (dx << 1) - (dy << 1);
    }

    do {
	PutPixel();
	if (dx > dy) {
	     x += m;
	} else {
	    y += n;
	}
	if (error < 0) {
	    error += c1;
	} else {
	    if (dx > dy) {
		y += n;
	    } else {
		x += m;
	    }
	    error += c2;
	}
    } while (!(x == x1 && y == y1));
    PutPixel();
    _MoveTo(x1, y1);
}

void Dash::_Line(Coord x1, Coord y1, Coord x2, Coord y2) {
    _Init();
    _MoveTo(x1, y1);
    _LineTo(x2, y2);
    _Finish();
}

void Dash::_Circle (Coord mx, Coord my, int r) {
    double x, y, x0, y0;
    double p, c1, c2;

    _Init();
    int n = GetSteps(r);
    p = Pix2/(n-1);
    c1 = cos(p); c2 = sin(p);
    x0 = mx + r; y0 = my;
    _MoveTo(x0, y0);
    for (int i = 1; i < n; i++) {
	x = mx + (x0-mx)*c1 - (y0-my)*c2;
	y = my + (x0-mx)*c2 + (y0-my)*c1;
	_LineTo(x, y);
	x0 = x; y0 = y;
    }
    _Finish();
}

void Dash::_Ellipse (Coord mx, Coord my, int a, int b) {
    double p, c1, c2, c3, s1, s2, s3;
    int x0, y0, x, y, x1, y1;
    double temp;

    _Init();
    int n = GetSteps(a);
    p = Pix2/(n-1);
    c1 = 1; s1 = 0;
    c2 = cos(p); s2 = sin(p);
    c3 = 1; s3 = 0;
    _MoveTo(mx + a*c3*c1 - b*s3*s1, my + a*c3*s1 + b*s3*s2);
    for (int i = 0; i < n; i++) {
	x1 = a*c3; y1 = b*s3;
	x = mx + x1*c1 - y1*s1;
	y = my + x1*s1 + y1*c1;
	_LineTo(x, y);
	temp = c3*c2 - s3*s2;
	s3 = s3*c2 + c3*s2; c3 = temp;
    }
    _Finish();
}

void Dash::_Rect (Coord left, Coord bottom, Coord right, Coord top) {
    _Init();
    _MoveTo(left, bottom);
    _LineTo(left, top);
    _LineTo(right, top);
    _LineTo(right, bottom);
    _LineTo(left, bottom);
    _Finish();
}

void Dash::_Polygon (Coord x[], Coord y[], int count) {
    _Init();
    _MoveTo(x[0], y[0]);
    for (int i = 1; i < count; i++) {
	_LineTo(x[i], y[i]);
    }
    _LineTo(x[0], y[0]);
    _Finish();
}

void Dash::_PolyLine (Coord x[], Coord y[], int count) {
    _Init();
    _MoveTo(x[0], y[0]);
    for (int i = 1; i < count; i++) {
	_LineTo(x[i], y[i]);
    }
    _Finish();
}

void Dash::Point (Coord xpos, Coord ypos) {
    x = xpos;
    y = ypos;
    _Init();
    PutPixel();
    _Finish();
}

void Dash::Line(Coord x1, Coord y1, Coord x2, Coord y2) {
    _Line(x1, y1, x2, y2);
}

void Dash::Circle (Coord mx, Coord my, int r) {
    _Circle(mx, my, r);
}

void Dash::Ellipse (Coord mx, Coord my, int a, int b) {
    _Ellipse(mx, my, a, b);
}

void Dash::Rect (Coord left, Coord bottom, Coord right, Coord top) {
    _Rect(left, bottom, right, top);
}

void Dash::Polygon (Coord x[], Coord y[], int c) {
    _Polygon(x, y, c);
}

void Dash::PolyLine (Coord x[], Coord y[], int c) {
    _PolyLine(x, y, c);
}

/*******************************************************************/

void PixelDash::_Init () {
    hDC = GetDC((HWND)canvas->Id());
    SelectPalette(hDC, _palette->GetPalette(), 0);
    RealizePalette(hDC);
    if (rep->clip) {
	holdRgn = SelectObject(hDC, rep->hRgn);
    }
}

void PixelDash::_Finish () {
    if (rep->clip) {
	SelectObject(hDC, holdRgn);
    }
    ReleaseDC((HWND)canvas->Id(), hDC);
}

void PixelDash::PutPixel() {
    if (set < 0) {
	    set++;
	    if (rep->fillbg) {
		SetPixel(hDC, x, y, background);
	    }
	    return;
    }
    if (set == 0) {
	    set = pattern[++index%count];
	    set = (index & Bit_1) ? -set : set;
	    PutPixel();
	    return;
    }
    SetPixel(hDC, x, y, foreground);
    set--;
}

/*******************************************************************/

BitmapDash::BitmapDash (int w) {
    width = w;
}

void BitmapDash::_Init () {
    hDC = GetDC((HWND)canvas->Id());
    SelectPalette(hDC, _palette->GetPalette(), 0);
    RealizePalette(hDC);

    hBitmap = CreateBitmap(width+1, width+1, 1, 1, NULL);
    hMemDC  = CreateCompatibleDC(hDC);
    holdBitmap = SelectObject(hMemDC, hBitmap);
    PatBlt(hMemDC, 0, 0, width, width, BLACKNESS);
    holdBrush1 = SelectObject(hMemDC, GetStockObject(WHITE_BRUSH));
    SetTextColor(hMemDC, RGB(255, 255, 255));
    ::Ellipse(hMemDC, -1, -1, width+1, width+1);

    hBrush = CreateSolidBrush(foreground);
    holdBrush = SelectObject(hDC, hBrush);
    if (rep->clip) {
	holdRgn = SelectObject(hDC, rep->hRgn);
    }

    offset = width >> 1;
    offset = (offset < 1) ? 0 : offset;
}

void BitmapDash::_Finish () {
    SelectObject(hMemDC, holdBitmap);
    SelectObject(hMemDC, holdBrush1);
    DeleteDC(hMemDC);
    DeleteObject(hBitmap);
    if (rep->clip) {
	SelectObject(hDC, holdRgn);
    }
    SelectObject(hDC, holdBrush);
    DeleteObject(hBrush);
    ReleaseDC((HWND)canvas->Id(), hDC);
}

void BitmapDash::PutPixel() {
    if (set < 0) {
	    set++;
	    return;
    }
    if (set == 0) {
	    set = pattern[++index%count];
	    set = (index & Bit_1) ? -set : set;
	    PutPixel();
	    return;
    }
    BitBlt(
	hDC, x - offset, y - offset, width, width, hMemDC, 0, 0, 0xE20746L
    );
    set--;
}

void BitmapDash::SaveSettings () {
    for (int i = 0; i < count; i++) {
	pattern[i] = -pattern[i];
    }
    save_foreground = foreground;
    foreground = background;
}

void BitmapDash::RestoreSettings () {
    for (int i = 0; i < count; i++) {
	pattern[i] = -pattern[i];
    }
    foreground = save_foreground;
    set = pattern[0];
    index = 0;
}

void BitmapDash::Line(Coord x1, Coord y1, Coord x2, Coord y2) {
    if (rep->fillbg) {
	SaveSettings();
	_Line(x1, y1, x2, y2);
	RestoreSettings();
    }
    _Line(x1, y1, x2, y2);
}

void BitmapDash::Circle (Coord mx, Coord my, int r) {
    if (rep->fillbg) {
	SaveSettings();
	_Circle(mx, my, r);
	RestoreSettings();
    }
    _Circle(mx, my, r);
}

void BitmapDash::Ellipse (Coord mx, Coord my, int a, int b) {
    if (rep->fillbg) {
	SaveSettings();
	_Ellipse(mx, my, a, b);
	RestoreSettings();
    }
    _Ellipse(mx, my, a, b);
}

void BitmapDash::Rect (Coord left, Coord bottom, Coord right, Coord top) {
    if (rep->fillbg) {
	SaveSettings();
	_Rect(left, bottom, right, top);
	RestoreSettings();
    }
    _Rect(left, bottom, right, top);
}

void BitmapDash::Polygon (Coord x[], Coord y[], int c) {
    if (rep->fillbg) {
	SaveSettings();
	_Polygon(x, y, c);
	RestoreSettings();
    }
    _Polygon(x, y, c);
}

void BitmapDash::PolyLine (Coord x[], Coord y[], int c) {
    if (rep->fillbg) {
	SaveSettings();
	_PolyLine(x, y, c);
	RestoreSettings();
    }
    _PolyLine(x, y, c);
}


