/*
Seat Number 07

07VIEW.CPP - Main program (this file)

Standart modules:
Module for standart I/O functions : stdio.h
Module for keyboard funstions : conio.h
Module for controling process (only for exit function) : process.h
Module for memory menagment (only for setmem function) : mem.h

Supported levels:

	Level 1
    Level 2
    Level 3
    Level 4

Description of algoryth:
    First, my program operates with ALL levels in the same way.
When input is read, the blocks are drawn on the screen with the appropriate
color. As two areas belong to the same region if and only if they are with
same color, I used this to determine if two are from same region. Selecting
of region is done with ONE "passing" thru the screen (It is possible to be
done with less then ONE passing - see below). For each point from the selected
regionI check if there are neighboring pixel from different region, outside
the screen or not belonging to any region. If any of these three conditions
is true i mark pixel with one special color (from 240 to 255) - this is not a
color of region. That guarantees me that border of whole region will be
highlighted. The highlighting itself is done by shifting the definitions of
special colors. Unselection is done by marking the pixels with special color
with color of selected region.
    When I read rectangles from input I remember for every region where is its
"extreme" points. That means most left, upper, right and bottom coordinate.
When I select region I use this to minimize checkings.
*/
#include <stdio.h>
#include <conio.h>
#include <process.h>
#include <mem.h>

unsigned char* video_mem=(unsigned char*)0xA0000000L; //video memory
int DX[4]={-1,0,1,0}; //this is one way to check all neighboring pixels
int DY[4]={0,1,0,-1};
unsigned char pal[768]; //converted palette
int minx[240],miny[240],maxx[240],maxy[240]; //arrays with extreme points

//a good way to  operate with video memory
#define vmem(x,y) (video_mem[((long)y)*320L+x])

int level; //not important
int _MX,_MY; //mouse position
char _B1,_B2,_B3; //mouse bittons status
int last_reg=0; //last operated region
int points; //number of points in highlighted border (not important)
FILE *inf; //input file

//initializing video mode 320x200x256
void initmode(){
    asm{
        mov ax,0x0013
        int 0x10
    	}
    setmem(video_mem,64000U,0);
	}

//initializing mode 80x25x16
void donemode(){
    asm{
        mov ax,0x0003
        int 0x10
    	}
	}

//drawing filled rectangle with specific color
void bar(int x1,int y1,int x2,int y2,unsigned char col){
	for(int y=y1;y<=y2;y++)
    	for(int x=x1;x<=x2;x++)
            vmem(x,y)=col;
	}

//Hiding of mouse pointer
void m_hide(){
    asm{
    	mov	ax,0x0002
        int	0x33
    	}
	}

//Showing of mouse pointer
void m_show(){
    asm{
    	mov	ax,0x0001
        int	0x33
    	}
	}

//initializing and showing mouse pointer
void mouse_init(){
    int fl;
    asm{
        mov	ax,0
        int	0x33
        mov	[fl],ax
    	}
    if(!fl){
        donemode();
    	printf("Mouse driver not installed !\n");
        exit(1);
        }
    asm{
        mov	ax,0x0001
        int	0x33
    	}
	}

//Getting mouse status and writing it in appropriate variables
void get_mstat(){
    int mx,my,bt;
    asm{
        mov	ax,0x0003
        int	0x33
        mov	[mx],cx
        mov	[my],dx
        mov	[bt],bx
    	}
    _MX=mx/2;_MY=my;
    _B1=bt&1;
    _B2=(bt>>1)&1;
    _B3=(bt>>2)&1;
    }

//Setting palette with registers - the best and fastest way to do it
void dump_pal(unsigned char*pal){
    asm{
        mov	dx,0x3C8
        lds	si,pal
        mov	al,0
        out	dx,al
        inc	dx
        mov	cx,768
    	}
lab_1:
	asm{
        lodsb
        out	dx,al
        dec	cx
        jnz	lab_1
    	}
	}

//*************************************End of low level functions
//reading information from input file
void read_file(){
    for(int i=0;i<240;i++){
    	minx[i]=miny[i]=400;
        maxx[i]=maxy[i]=0;
        }
    if(fscanf(inf,"MDF/%d",&level)<1){    //if invalid data occurs
        printf("Invalid input file !\n");
        fclose(inf);
        exit(1);
    	}
    if(level<1||level>4){
        printf("Invalid level number !\n");
        exit(1);
    	}
	int x1,x2,y1,y2,idx;
    while(!feof(inf)){
        fscanf(inf,"%d %d %d %d %d",&idx,&x1,&y1,&x2,&y2);
        bar(x1,y1,x2,y2,idx);
        if(x1<minx[idx])minx[idx]=x1;
        if(y1<miny[idx])miny[idx]=y1;
        if(x2>maxx[idx])maxx[idx]=x2;
        if(y2>maxy[idx])maxy[idx]=y2;
    	}
    }

//generate and converts palette
void gen_pal(){
    pal[0]=pal[1]=pal[2]=0;
    for(int i=1;i<240;i++){
        pal[i*3]=(((((unsigned long)i>>5)*255)/7)*64)/256;
        pal[i*3+1]=((((((unsigned long)i>>2)&7)*255)/7)*64)/256;
        pal[i*3+2]=(((((unsigned long)i&3)*255)/3)*64)/256;
        }
//spacial colors
    for(i=240;i<255;i++)
        pal[i*3]=pal[i*3+1]=pal[i*3+2]=(i-240)*4;
    pal[765]=pal[766]=pal[767]=63;
    dump_pal(pal);
	}

//the "main" function - see description of algorythm
void set_region(){
    last_reg=vmem(_MX,_MY); //getting the color(number) of region
    if(!last_reg)return;
    int mx,my;
    points=0;
    unsigned char tt;
    for(int y=miny[last_reg];y<=maxy[last_reg];y++)
    	for(int x=minx[last_reg];x<=maxx[last_reg];x++)
        	if(vmem(x,y)==last_reg){
                for(int i=0;i<4;i++){
                    mx=x+DX[i];  //mx,my is position of neighboring pixel
                    my=y+DY[i];
                    if((mx<0)||(mx>=320)||(my<0)||(my>=200)){
                        vmem(x,y)=((points++)%16)+240;
                        break;
                    	}
                    tt=vmem(mx,my);
                    if((tt<240)&&(tt!=last_reg)){
                        vmem(x,y)=((points++)%16)+240;
                        break;
                    	}
            	    }
	        	}
	}

//shifting palette of special colors - I set all palette again, because its
//faster when using video registers
void shift_pal(){
    for(int i=240;i<=255;i++)
        pal[i*3]=pal[i*3+1]=pal[i*3+2]=(pal[i*3]+4)%64;
    dump_pal(pal);
	}

//unselecting region
void unselect_region(){
	for(int y=miny[last_reg];y<=maxy[last_reg];y++)
    	for(int x=minx[last_reg];x<=maxx[last_reg];x++)
            if(vmem(x,y)>239)vmem(x,y)=last_reg;
	}

//the entry point
void main(int argc, char *arg[]){
    if(argc<2){
        printf("Usage : 07mview.exe <filename> !\n");
        return;
    	}
    inf=fopen(arg[1],"rt");
    if(inf==NULL){
    	printf("Cannot open file\n");
        return;
    	}
    printf("Proceeding : %s\n",arg[1]);
    initmode(); //set mode
    gen_pal();  //generate palette
    read_file(); //read input
    mouse_init(); //set mouse
    long cc=0;  //cycles of waiting for animating border
    for(char ch;;){
        if(kbhit()){
            ch=getch();
            if(ch==27)break;
            if(!ch)getch();
            }
        get_mstat(); //check mouse
        if(_B1){     //only left button operates
            m_hide();
            if(last_reg)unselect_region();
        	set_region();
            m_show();
            }
        else{
        	cc++;
            if(cc>1000){
        		shift_pal(); //animate border
                cc=0;
                }
            }
    	}
    donemode(); //set text mode
    fclose(inf); //close input file
	}
