/*
 * File: abclib.c
 * Part of the ABClock package
 * (c) Peter Kleiweg
 *
 * 2000/08/15: version 1.0
 * 2000/08/06: version 0.9
 * 2000/08/04: version 0.1
 *
 * This is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2,
 * or (at your option) any later version.
 *
 */

#include <math.h>
#include <stdio.h>
#include <stdlib.h>

#include "abclib.h"

int
    ABC_size,
    ABC_xsize = 0,
    ABC_ysize = 0,
    ABC_hsize,
    ABC_msize,
    ABC_mwidth,
    ABC_mskip,
    ABC_hpart,
    ABC_mpart,
    ABC_border,
    ABC_bborder,
    ABC_edge,
    ABC_a1,
    ABC_a2,
    ABC_a3,
    ABC_z1,
    ABC_z2,
    ABC_z3,
    ABC_m1,
    ABC_m2,
    ABC_m3,
    ABC_block_x,
    ABC_block_y;

void
    ABC_sizes (int width, int height, int border),
    ABC_top    (int x, float f),
    ABC_bottom (int x, float f),
    ABC_left   (int y, float f),
    ABC_right  (int y, float f);

#define ABC_RECT(x1, y1, x2, y2) (ABC_Rect \
            (ABC_block_x + x1, ABC_block_y + y1, \
             ABC_block_x + x2, ABC_block_y + y2))

void ABC_sizes (int width, int height, int border)
{
    int
	tsize;

    if (width < ABC_MINSIZE || height < ABC_MINSIZE) {
	fputs ("ABClib: too small\n", stderr);
	exit (1);
    }

    if (border > 0 &&
        (width - 2 * border < ABC_MINSIZE ||
         height - 2 * border < ABC_MINSIZE)
    ) {
	fputs ("ABClib: border too large for size\n", stderr);
	exit (1);
    }

    if (width == ABC_xsize &&
        height == ABC_ysize &&
        (border == ABC_border || (border < 0 && ABC_border < 0))
    )
        return;

    ABC_xsize = width;
    ABC_ysize = height;
    ABC_border = border;

    ABC_size = (ABC_xsize < ABC_ysize) ? ABC_xsize : ABC_ysize;
    if (ABC_border < 0) {
        ABC_bborder = ABC_size / 60;
        if (ABC_bborder == 0 && ABC_size > ABC_MINSIZE + 1)
            ABC_bborder = 1;
    } else
	ABC_bborder = ABC_border;
 
    tsize = ABC_size - 2 * ABC_bborder;
    ABC_edge = (tsize + 20) / 30;
    ABC_mpart = 5 * ((tsize + 9) / 15);
    ABC_mskip = (ABC_mpart + 5) / 15;
    ABC_mwidth = ABC_mpart / 5 - ABC_mskip;
    tsize = ABC_size - 2 * ABC_bborder - ABC_mpart;
    ABC_hsize = (tsize - 2 * ABC_edge) / 3;
    ABC_hpart = 3 * ABC_hsize + 2 * ABC_edge;
    tsize = ABC_hpart + ABC_mpart;
    ABC_msize = (ABC_hpart - 2 * (ABC_hpart / 5)) / 3;
    if ((ABC_msize % 2) != (ABC_hpart % 2))
	ABC_msize--;
    while (ABC_msize < (ABC_hpart - 3 * ABC_msize) / 2)
	ABC_msize += 2;

    ABC_a1 = ABC_edge;
    ABC_z1 = ABC_edge + ABC_hsize - 1;
    ABC_a2 = ABC_edge + ABC_hsize;
    ABC_z2 = ABC_edge + 2 * ABC_hsize - 1;
    ABC_a3 = ABC_edge + 2 * ABC_hsize;
    ABC_z3 = ABC_edge + 3 * ABC_hsize - 1;

    ABC_m1 = 0;
    ABC_m2 = (ABC_hpart - ABC_msize) / 2;
    ABC_m3 = ABC_hpart - ABC_msize;
}

/*
 * put minutes f at (x, top)
 */
void ABC_top (int x, float f)
{
    int
	i,
	m1,
	m,
	y;

    f = fabs (f);
    f = 5.0 - f;
    y = 2 * ABC_edge + 3 * ABC_hsize + ABC_mskip;
    for (i = 0; i < 5; i++) {
	if ((m = (int) (ABC_msize * f + .5)) > ABC_msize)
	    m = ABC_msize;
	if (m < 1)
	    return;
	m1 = (ABC_msize - m) / 2;
	ABC_RECT (x + m1, y, x + m1 + m - 1, y + ABC_mwidth - 1);
	f -= 1.0;
	y += (ABC_mskip + ABC_mwidth);
    }
}

/*
 * put minutes f at (x, bottom)
 */
void ABC_bottom (int x, float f)
{
    int
	i,
	m,
	m1,
	y;

    f = fabs (f);
    f = 5.0 - f;
    y = - ABC_mskip - ABC_mwidth;
    for (i = 0; i < 5; i++) {
	if ((m = (int) (ABC_msize * f + .5)) > ABC_msize)
	    m = ABC_msize;
	if (m < 1)
	    return;
	m1 = (ABC_msize - m) / 2;
	ABC_RECT (x + m1, y, x + m1 + m - 1, y + ABC_mwidth - 1);
	f -= 1.0;
	y -= (ABC_mskip + ABC_mwidth);
    }
}

/*
 * put minutes at (right, y)
 */
void ABC_right (int y, float f)
{
    int
	i,
	m,
	m1,
	x;

    f = fabs (f);
    f = 5.0 - f;
    x = 2 * ABC_edge + 3 * ABC_hsize + ABC_mskip;
    for (i = 0; i < 5; i++) {
	if ((m = (int) (ABC_msize * f + .5)) > ABC_msize)
	    m = ABC_msize;
	if (m < 1)
	    return;
	m1 = (ABC_msize - m) / 2;
	ABC_RECT (x, y + m1, x + ABC_mwidth - 1, y + m1 + m - 1);
	f -= 1.0;
	x += (ABC_mskip + ABC_mwidth);
    }
}

/*
 * put minutes at (left, y)
 */
void ABC_left (int y, float f)
{
    int
	i,
	m,
	m1,
	x;

    f = fabs (f);
    f = 5.0 - f;
    x = - ABC_mskip - ABC_mwidth;
    for (i = 0; i < 5; i++) {
	if ((m = (int) (ABC_msize * f + .5)) > ABC_msize)
	    m = ABC_msize;
	if (m < 1)
	    return;
	m1 = (ABC_msize - m) / 2;
	ABC_RECT (x, y + m1, x + ABC_mwidth - 1, y + m1 + m - 1);
	f -= 1.0;
	x -= (ABC_mskip + ABC_mwidth);
    }
}

void ABC_Make (int hour, int min, int sec, int width, int height, int border)
{
    float
	f,
	x,
        y,
	xm,
	ym;
    int
	i;

    ABC_sizes (width, height, border);

    if ((hour %= 12) < 0)
	hour += 12;
    if ((min %= 60) < 0)
	min += 60;
    if ((sec %= 60) < 0)
	sec += 60;

    /* set color to background */
    ABC_SetColor (0);

    /* clear image */
    ABC_block_x = ABC_block_y = 0;
    ABC_RECT (0, 0, ABC_xsize - 1, ABC_ysize - 1);

    /* determine where to put the square, to leave room for minutes */

    if ((xm = ABC_hpart + 2 * ABC_mpart + 2 * ABC_bborder - ABC_xsize) < 0)
        xm = 0;
    if ((ym = ABC_hpart + 2 * ABC_mpart + 2 * ABC_bborder - ABC_ysize) < 0)
	ym = 0;

    if (min < 5 || min >= 55) {
        if ((f = min) > 5)
	    f -= 60.0;
	f += ((float) sec) / 60.0;
	x = - f / 5.0 * xm;
    } else if (min < 25)
	x = -xm;
    else if (min < 35) {
	f = ((float) sec) / 60.0 + (float) min;
	x = (f - 30.0) / 5.0 * xm;
    } else
	x = xm;

    if (min < 10 || min >= 50)
	y = -ym;
    else if (min < 20) {
	f = ((float) sec) / 60.0 + (float) min;
	y = (f - 15.0) / 5.0 * ym;
    } else if (min < 40)
	y = ym;
    else {
	f = ((float) sec) / 60.0 + (float) min;
	y = (45.0 - f) / 5.0 * ym;
    }

    ABC_block_x = (x + ((float) (ABC_xsize - ABC_hpart))) / 2.0 + .5;
    ABC_block_y = (y + ((float) (ABC_ysize - ABC_hpart))) / 2.0 + .5;

    /* set color to square border */
    ABC_SetColor (2);

    /* paint square border */
    ABC_RECT (0,
	      0,
	      2 * ABC_edge + 3 * ABC_hsize - 1,
	      2 * ABC_edge + 3 * ABC_hsize - 1);

    /* set color to inside square */
    ABC_SetColor (1);

    /* paint inside square */
    ABC_RECT (ABC_edge,
	      ABC_edge,
              ABC_edge + 3 * ABC_hsize - 1,
	      ABC_edge + 3 * ABC_hsize - 1);

    /* set color to hours */
    ABC_SetColor (3);

    /* paint hours */
    f = ((float) hour) + ((float) min) / 60.0 + ((float) sec) / 3600.0;
    switch (hour) {
	case 0:
	    i = (int) (f * ((float) ABC_hsize) + .5);
	    ABC_RECT (ABC_a2, ABC_a2 + i, ABC_z2, ABC_z3);
	    ABC_RECT (ABC_a3, ABC_a3, ABC_z2 + i, ABC_z3);
	    break;
	case 1:
	    i = (int) ((f - 1.0) * ((float) ABC_hsize) + .5);
	    ABC_RECT (ABC_a2 + i, ABC_a3, ABC_z3, ABC_z3);
	    ABC_RECT (ABC_a3, ABC_a3 - i, ABC_z3, ABC_z2);
	    break;
	case 2:
	    i = (int) ((f - 2.0) * ((float) ABC_hsize) + .5);
	    ABC_RECT (ABC_a3, ABC_a2, ABC_z3, ABC_z3 - i);
	    ABC_RECT (ABC_a3 - i, ABC_a2, ABC_z2, ABC_z2);
	    break;
	case 3:
	    i = (int) ((f - 3.0) * ((float) ABC_hsize) + .5);
	    ABC_RECT (ABC_a2 + i, ABC_a2, ABC_z3, ABC_z2);
	    ABC_RECT (ABC_a3, ABC_a2 - i, ABC_z3, ABC_z2);
	    break;
	case 4:
	    i = (int) ((f - 4.0) * ((float) ABC_hsize) + .5);
	    ABC_RECT (ABC_a3, ABC_a1, ABC_z3, ABC_z2 - i);
	    ABC_RECT (ABC_a3 - i, ABC_a1, ABC_z2, ABC_z1);
	    break;
	case 5:
	    i = (int) ((f - 5.0) * ((float) ABC_hsize) + .5);
	    ABC_RECT (ABC_a2, ABC_a1, ABC_z3 - i, ABC_z1);
	    ABC_RECT (ABC_a2, ABC_a2, ABC_z2, ABC_z1 + i);
	    break;
	case 6:
	    i = (int) ((f - 6.0) * ((float) ABC_hsize) + .5);
	    ABC_RECT (ABC_a2, ABC_a1, ABC_z2, ABC_z2 - i);
	    ABC_RECT (ABC_a2 - i, ABC_a1, ABC_z1, ABC_z1);
	    break;
	case 7:
	    i = (int) ((f - 7.0) * ((float) ABC_hsize) + .5);
	    ABC_RECT (ABC_a1, ABC_a1, ABC_z2 - i, ABC_z1);
	    ABC_RECT (ABC_a1, ABC_a2, ABC_z1, ABC_z1 + i);
	    break;
	case 8:
	    i = (int) ((f - 8.0) * ((float) ABC_hsize) + .5);
	    ABC_RECT (ABC_a1, ABC_a1 + i, ABC_z1, ABC_z2);
	    ABC_RECT (ABC_a2, ABC_a2, ABC_z1 + i, ABC_z2);
	    break;
	case 9:
	    i = (int) ((f - 9.0) * ((float) ABC_hsize) + .5);
	    ABC_RECT (ABC_a1, ABC_a2, ABC_z2 - i, ABC_z2);
	    ABC_RECT (ABC_a1, ABC_a3, ABC_z1, ABC_z2 + i);
	    break;
	case 10:
	    i = (int) ((f - 10.0) * ((float) ABC_hsize) + .5);
	    ABC_RECT (ABC_a1, ABC_a2 + i, ABC_z1, ABC_z3);
	    ABC_RECT (ABC_a2, ABC_a3, ABC_z1 + i, ABC_z3);
	    break;
	case 11:
	    i = (int) ((f - 11.0) * ((float) ABC_hsize) + .5);
	    ABC_RECT (ABC_a1 + i, ABC_a3, ABC_z2, ABC_z3);
	    ABC_RECT (ABC_a2, ABC_a3 - i, ABC_z2, ABC_z3);
	    break;
    }

    /* set color to minutes */
    ABC_SetColor (4);

    /* paint minutes */
    f = ((float) min) + ((float) sec) / 60.0;
    if (min >= 55 || min < 5)
	ABC_top (ABC_m2, (min < 5) ? f : (f - 60.0));
    if (min < 10)
        ABC_top (ABC_m3, f - 5.0);
    if (min >= 5 && min < 15)
	ABC_right (ABC_m3, f - 10.0);
    if (min >= 10 && min < 20)
	ABC_right (ABC_m2, f - 15.0);
    if (min >= 15 && min < 25)
	ABC_right (ABC_m1, f - 20.0);
    if (min >= 20 && min < 30)
	ABC_bottom (ABC_m3, f - 25.0);
    if (min >= 25 && min < 35)
	ABC_bottom (ABC_m2, f - 30.0);
    if (min >= 30 && min < 40)
	ABC_bottom (ABC_m1, f - 35.0);
    if (min >= 35 && min < 45)
	ABC_left (ABC_m1, f - 40.0);
    if (min >= 40 && min < 50)
	ABC_left (ABC_m2, f - 45.0);
    if (min >= 45 && min < 55)
	ABC_left (ABC_m3, f - 50.0);
    if (min >= 50)
	ABC_top (ABC_m1, f - 55.0);
}
