/**
 * $Id: drawnla.c,v 1.6 2003/07/21 21:21:15 ton Exp $
 *
 * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
 *
 * This program 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
 * of the License, or (at your option) any later version. The Blender
 * Foundation also sells licenses for use in proprietary software under
 * the Blender License.  See http://www.blender.org/BL/ for information
 * about this.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
 * All rights reserved.
 *
 * The Original Code is: all of this file.
 *
 * Contributor(s): none yet.
 *
 * ***** END GPL/BL DUAL LICENSE BLOCK *****
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#ifdef _WIN32
#pragma warning (once : 4761)
#include "BLI_winstuff.h"
#endif

#include "BMF_Api.h"

#include <stdlib.h>
#include <stdio.h>
#include "BSE_drawnla.h"
#include "BSE_drawipo.h"
#include "BSE_editnla_types.h"

#include "BIF_gl.h"
#include "BIF_resources.h"
#include "BIF_screen.h"
#include "BIF_mywindow.h"
#include "BIF_glutil.h"

#include "DNA_view3d_types.h"
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
#include "DNA_scene_types.h"
#include "DNA_object_types.h"
#include "DNA_curve_types.h"
#include "DNA_ipo_types.h"
#include "DNA_action_types.h"
#include "DNA_nla_types.h"
#include "DNA_constraint_types.h"

#include "BLI_blenlib.h"
#include "MEM_guardedalloc.h"
#include "BKE_global.h"

#include "BDR_drawaction.h"
#include "BDR_editcurve.h"

#include "blendef.h"
#include "interface.h"

/* Local function prototypes */
static void draw_nlastrips(SpaceNla *snla);
static void draw_nlatree(void);

int count_nla_levels(void);
int nla_filter (Base* base, int flags);

#define TESTBASE_SAFE(base)	((base)->flag & SELECT)

/* Implementation */
static void draw_nlatree(void)
{

	short ofsx, ofsy = 0; 
	Base *base;
	float	x, y;
	bActionStrip *strip;
	bConstraintChannel *conchan;

	myortho2		(0,	NLAWIDTH, G.v2d->cur.ymin, G.v2d->cur.ymax);	//	Scaling

	/* Blank out the area */
	if(curarea->winx>SCROLLB+10 && curarea->winy>SCROLLH+10) {
		if(G.v2d->scroll) {	
			ofsx= curarea->winrct.xmin;	
			ofsy= curarea->winrct.ymin;
			glViewport(ofsx,  ofsy+G.v2d->mask.ymin-SCROLLB, NLAWIDTH, ( ofsy+G.v2d->mask.ymax)-( ofsy+G.v2d->mask.ymin-SCROLLB)); 
			glScissor(ofsx,  ofsy+G.v2d->mask.ymin-SCROLLB, NLAWIDTH, ( ofsy+G.v2d->mask.ymax)-( ofsy+G.v2d->mask.ymin-SCROLLB));
		}
	}
	
	glClearColor(.6, .6, .6, 0.0); 
	glClear(GL_COLOR_BUFFER_BIT);

	/* Clip to the scrollable area */

	glColor3ub(0x00, 0x00, 0x00);
	
	x = 0.0;

	y = count_nla_levels();

	y*= (NLACHANNELHEIGHT+NLACHANNELSKIP);

	
	for (base=G.scene->base.first; base; base=base->next){
		if (nla_filter(base, 0)){		
			cpack (0xAAAAAA);
			glRectf(x,  y-NLACHANNELHEIGHT/2,  (float)NLAWIDTH,  y+NLACHANNELHEIGHT/2);

			/* Draw the name / ipo timeline*/
			if (TESTBASE_SAFE(base))
				cpack(0xFFFFFF);
			else
				cpack (0x000000);
			glRasterPos2f(x+16,  y-4);

			BMF_DrawString(G.font, base->object->id.name+2);
			
			/* Draw the constraint ipos */
			for (conchan = base->object->constraintChannels.first; conchan; conchan=conchan->next){
				y-=NLACHANNELHEIGHT+NLACHANNELSKIP;
				cpack (0x888888);
				glRectf(x+16,  y-NLACHANNELHEIGHT/2,  (float)NLAWIDTH,  y+NLACHANNELHEIGHT/2);
				
				if (conchan->flag & CONSTRAINT_CHANNEL_SELECT)
					cpack(0xFFFFFF);
				else
					cpack (0x000000);

				glRasterPos2f(x+32,  y-4);
				BMF_DrawString(G.font, conchan->name);
			}
	
			/* Draw the action timeline */
			if (ACTIVE_ARMATURE(base)){
				glRasterPos2f(x,  y-8);				
				BIF_draw_icon(ICON_DOWNARROW_HLT);
				y-=NLACHANNELHEIGHT+NLACHANNELSKIP;
				
				if (base->object->action){
					cpack (0x888888);
					glRectf(x+16,  y-NLACHANNELHEIGHT/2,  (float)NLAWIDTH,  y+NLACHANNELHEIGHT/2);

					if (TESTBASE_SAFE(base))
						cpack(0xFFFFFF);
					else
						cpack (0x000000);
					glRasterPos2f(x+32,  y-4);
					BMF_DrawString(G.font, base->object->action->id.name+2);
				}
			}
			y-=NLACHANNELHEIGHT+NLACHANNELSKIP;

			/* Draw the nla strips */
			if (base->object->type==OB_ARMATURE){
				for (strip = base->object->nlastrips.first; strip; strip=strip->next){
					cpack (0x666666);
					glRectf(x+32,  y-NLACHANNELHEIGHT/2,  (float)NLAWIDTH,  y+NLACHANNELHEIGHT/2);

					if (TESTBASE_SAFE(base))
						cpack(0xFFFFFF);
					else
						cpack (0x000000);

	// why this test? check freeing mem when deleting strips? (ton)
					if(strip->act) {
						glRasterPos2f(x+48,  y-4);
						BMF_DrawString(G.font, strip->act->id.name+2);

						y-=(NLACHANNELHEIGHT+NLACHANNELSKIP);
					}
				}
			}
		}
	}
	
	myortho2		(0,	NLAWIDTH, 0, ( ofsy+G.v2d->mask.ymax)-( ofsy+G.v2d->mask.ymin-SCROLLB));	//	Scaling

	glShadeModel(GL_SMOOTH);
 
	y=9;

#if 1
	/* Draw sexy shaded block thingies */
	glEnable (GL_BLEND);
	glBegin(GL_QUAD_STRIP);
	glColor4ub (0x99,0x99,0x99,0x00);
	glVertex2f (0,SCROLLB*2-y);
	glVertex2f (NLAWIDTH,SCROLLB*2-y);

	glColor4ub (0x99,0x99,0x99,0xFF);
	glVertex2f (0,SCROLLB-y);
	glVertex2f (NLAWIDTH,SCROLLB-y);

	glColor4ub (0x99,0x99,0x99,0xFF);
	glVertex2f (0,0-y);
	glVertex2f (NLAWIDTH,0-y);

	glEnd();

	glDisable (GL_BLEND);
#endif

	glShadeModel(GL_FLAT);
}

static void draw_nlastrips(SpaceNla *snla)
{
	rcti scr_rct;
	gla2DDrawInfo *di;
	Base *base;
	bConstraintChannel *conchan;
	
	float	y;
	
	/* Draw strips */

	scr_rct.xmin= snla->area->winrct.xmin + NLAWIDTH;
	scr_rct.ymin= snla->area->winrct.ymin + snla->v2d.mask.ymin-SCROLLB;
	scr_rct.xmax= snla->area->winrct.xmin + snla->v2d.hor.xmax;
	scr_rct.ymax= snla->area->winrct.ymin + snla->v2d.mask.ymax; 
	di= glaBegin2DDraw(&scr_rct, &G.v2d->cur);
	
	y=count_nla_levels();
	y*= (NLACHANNELHEIGHT+NLACHANNELSKIP);
	
	for (base=G.scene->base.first; base; base=base->next){
		Object *ob;
		bActionStrip *strip;
		int frame1_x, channel_y;
		
		ob=base->object;
		
		if (nla_filter(base, 0)){
			/* Draw the field */
			glEnable (GL_BLEND);
			if (TESTBASE_SAFE(base))
				glColor4b (0x11, 0x22, 0x55, 0x22);
			else
				glColor4b (0x55, 0x22, 0x11, 0x22);
			
			gla2DDrawTranslatePt(di, 1, y, &frame1_x, &channel_y);
			glRectf(0,  channel_y-NLACHANNELHEIGHT/2,  frame1_x,  channel_y+NLACHANNELHEIGHT/2);
			
			
			if (TESTBASE_SAFE(base))
				glColor4b (0x11, 0x22, 0x55, 0x44);
			else
				glColor4b (0x55, 0x22, 0x11, 0x44);
			glRectf(frame1_x,  channel_y-NLACHANNELHEIGHT/2,   G.v2d->hor.xmax,  channel_y+NLACHANNELHEIGHT/2);
			
			glDisable (GL_BLEND);
			
			/* Draw the ipo */
			draw_object_channel(di, ob, 0, y);
			y-=NLACHANNELHEIGHT+NLACHANNELSKIP;
			
			/* Draw the constraints */
			for (conchan=ob->constraintChannels.first; conchan; conchan=conchan->next){
				glEnable (GL_BLEND);
				if (conchan->flag & CONSTRAINT_CHANNEL_SELECT)
					glColor4b (0x11, 0x22, 0x55, 0x22);
				else
					glColor4b (0x55, 0x22, 0x11, 0x22);
				
				gla2DDrawTranslatePt(di, 1, y, &frame1_x, &channel_y);
				glRectf(0,  channel_y-NLACHANNELHEIGHT/2+4,  frame1_x,  channel_y+NLACHANNELHEIGHT/2-4);
				
				
				if (conchan->flag & CONSTRAINT_CHANNEL_SELECT)
					glColor4b (0x11, 0x22, 0x55, 0x44);
				else
					glColor4b (0x55, 0x22, 0x11, 0x44);
				glRectf(frame1_x,  channel_y-NLACHANNELHEIGHT/2+4,   G.v2d->hor.xmax,  channel_y+NLACHANNELHEIGHT/2-4);
				
				glDisable (GL_BLEND);
				
				/* Draw the ipo */
				draw_ipo_channel(di, conchan->ipo, 0, y);
				y-=NLACHANNELHEIGHT+NLACHANNELSKIP;

			}
		}
		
		
		
		
		/* Draw the action strip */
		if (ACTIVE_ARMATURE(base)){
			
			/* Draw the field */
			glEnable (GL_BLEND);
			if (TESTBASE_SAFE(base))
				glColor4ub (0x11, 0x22, 0x55, 0x22);
			else
				glColor4ub (0x55, 0x22, 0x11, 0x22);
			gla2DDrawTranslatePt(di, 1, y, &frame1_x, &channel_y);
			glRectf(0,  channel_y-NLACHANNELHEIGHT/2+4,  frame1_x,  channel_y+NLACHANNELHEIGHT/2-4);
			
			
			if (TESTBASE_SAFE(base))
				glColor4ub (0x11, 0x22, 0x55, 0x44);
			else
				glColor4ub (0x55, 0x22, 0x11, 0x44);
			glRectf(frame1_x,  channel_y-NLACHANNELHEIGHT/2+4,   G.v2d->hor.xmax,  channel_y+NLACHANNELHEIGHT/2-4);
			
			glDisable (GL_BLEND);
			
			/* Draw the action keys */
			draw_action_channel(di, ob->action, 0, y);

			y-=NLACHANNELHEIGHT+NLACHANNELSKIP;
			
		}

		/* Draw the nla strips */
		if (ob->type==OB_ARMATURE){
			for (strip=ob->nlastrips.first; strip; strip=strip->next){
				int stripstart, stripend;
				int blendstart, blendend;
				unsigned char r,g,b;
				
				/* Draw rect */
				if (strip->flag & ACTSTRIP_SELECT){
					r = 0xFF; g=0xFF; b=0xAA;
				}
				else{
					r = 0xE4; g=0x9C; b=0xC6;
				}
				
				glColor4ub (r, g, b, 0xFF);
				
				gla2DDrawTranslatePt(di, strip->start+strip->blendin, y, &stripstart, &channel_y);
				gla2DDrawTranslatePt(di, strip->end-strip->blendout, y, &stripend, &channel_y);
				glRectf(stripstart,  channel_y-NLACHANNELHEIGHT/2+3,  stripend,  channel_y+NLACHANNELHEIGHT/2-3);
				
				
				/* Draw blendin */
				if (strip->blendin>0){
					glBegin(GL_TRIANGLES);
					
					gla2DDrawTranslatePt(di, strip->start, y, &blendstart, &channel_y);
					
					glColor4ub (r*0.75, g*0.75, b*0.75, 0xFF);
					glVertex2f(blendstart, channel_y-NLACHANNELHEIGHT/2+3);
					glVertex2f(stripstart, channel_y+NLACHANNELHEIGHT/2-3);
					glVertex2f(stripstart, channel_y-NLACHANNELHEIGHT/2+3);
					
					
					glEnd();
				}
				if (strip->blendout>0){
					glBegin(GL_TRIANGLES);
					gla2DDrawTranslatePt(di, strip->end, y, &blendend, &channel_y);
					glColor4ub (r*0.75, g*0.75, b*0.75, 0xFF);
					glVertex2f(blendend, channel_y-NLACHANNELHEIGHT/2+3);
					glVertex2f(stripend, channel_y+NLACHANNELHEIGHT/2-3);
					glVertex2f(stripend, channel_y-NLACHANNELHEIGHT/2+3);
					glEnd();
				}
				
				/* Draw border */
				glBegin(GL_LINE_STRIP);
				glColor4f(1, 1, 1, 0.5); 
				gla2DDrawTranslatePt(di, strip->start, y, &stripstart, &channel_y);
				gla2DDrawTranslatePt(di, strip->end, y, &stripend, &channel_y);
				
				glVertex2f(stripstart, channel_y-NLACHANNELHEIGHT/2+3);
				glVertex2f(stripstart, channel_y+NLACHANNELHEIGHT/2-3);
				glVertex2f(stripend, channel_y+NLACHANNELHEIGHT/2-3);
				glColor4f(0, 0, 0, 0.5); 
				glVertex2f(stripend, channel_y-NLACHANNELHEIGHT/2+3);
				glVertex2f(stripstart, channel_y-NLACHANNELHEIGHT/2+3);
				glEnd();
				
				glEnable (GL_BLEND);

				/* Show strip extension */
				if (strip->flag & ACTSTRIP_HOLDLASTFRAME){
					glColor4ub (r, g, b, 0x55);
					
					glRectf(stripend,  channel_y-NLACHANNELHEIGHT/2+4,  G.v2d->hor.xmax,  channel_y+NLACHANNELHEIGHT/2-2);
				}
				
				/* Show repeat */
				if (strip->repeat > 1.0 && !(strip->flag & ACTSTRIP_USESTRIDE)){
					float rep = 1;
					glBegin(GL_LINES);
					while (rep<strip->repeat){
						/* Draw line */	
						glColor4f(0, 0, 0, 0.5); 
						gla2DDrawTranslatePt(di, strip->start+(rep*((strip->end-strip->start)/strip->repeat)), y, &frame1_x, &channel_y);
						glVertex2f(frame1_x, channel_y-NLACHANNELHEIGHT/2+4);
						glVertex2f(frame1_x, channel_y+NLACHANNELHEIGHT/2-2);
						
						glColor4f(1.0, 1.0, 1.0, 0.5); 
						gla2DDrawTranslatePt(di, strip->start+(rep*((strip->end-strip->start)/strip->repeat)), y, &frame1_x, &channel_y);
						glVertex2f(frame1_x+1, channel_y-NLACHANNELHEIGHT/2+4);
						glVertex2f(frame1_x+1, channel_y+NLACHANNELHEIGHT/2-2);
						rep+=1.0;
					}
					glEnd();
					
				}
				glDisable (GL_BLEND);
				
				y-=(NLACHANNELHEIGHT+NLACHANNELSKIP);
			}
			
		}
	}
	glaEnd2DDraw(di);
	
}

void drawnlaspace(ScrArea *sa, void *spacedata)
{
	short ofsx = 0, ofsy = 0;
	
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) ;
	
	calc_scrollrcts(G.v2d, curarea->winx, curarea->winy);
	
	if(curarea->winx>SCROLLB+10 && curarea->winy>SCROLLH+10) {
		if(G.v2d->scroll) {	
			ofsx= curarea->winrct.xmin;	
			ofsy= curarea->winrct.ymin;
			glViewport(ofsx+G.v2d->mask.xmin,  ofsy+G.v2d->mask.ymin, ( ofsx+G.v2d->mask.xmax-1)-(ofsx+G.v2d->mask.xmin)+1, ( ofsy+G.v2d->mask.ymax-1)-( ofsy+G.v2d->mask.ymin)+1); 
			glScissor(ofsx+G.v2d->mask.xmin,  ofsy+G.v2d->mask.ymin, ( ofsx+G.v2d->mask.xmax-1)-(ofsx+G.v2d->mask.xmin)+1, ( ofsy+G.v2d->mask.ymax-1)-( ofsy+G.v2d->mask.ymin)+1);
		}
	}
	
	glClearColor(.45, .45, .45, 0.0); 
	glClear(GL_COLOR_BUFFER_BIT);
	
	myortho2 (G.v2d->cur.xmin, G.v2d->cur.xmax, G.v2d->cur.ymin, G.v2d->cur.ymax);
	
	/*	Draw backdrop */
	calc_ipogrid();	
	draw_ipogrid();

	/* Draw channel strips */
	draw_nlastrips(G.snla);

	/* Draw current frame */
	glViewport(ofsx+G.v2d->mask.xmin,  ofsy+G.v2d->mask.ymin, ( ofsx+G.v2d->mask.xmax-1)-(ofsx+G.v2d->mask.xmin)+1, ( ofsy+G.v2d->mask.ymax-1)-( ofsy+G.v2d->mask.ymin)+1); 
	glScissor(ofsx+G.v2d->mask.xmin,  ofsy+G.v2d->mask.ymin, ( ofsx+G.v2d->mask.xmax-1)-(ofsx+G.v2d->mask.xmin)+1, ( ofsy+G.v2d->mask.ymax-1)-( ofsy+G.v2d->mask.ymin)+1);
	myortho2 (G.v2d->cur.xmin, G.v2d->cur.xmax, G.v2d->cur.ymin, G.v2d->cur.ymax);
	draw_cfra_action();

	/* Draw scroll */
	mywinset(curarea->win);
	if(curarea->winx>SCROLLB+10 && curarea->winy>SCROLLH+10) {
		myortho2(-0.5, curarea->winx+0.5, -0.5, curarea->winy+0.5);
		if(G.v2d->scroll) drawscroll(0);
	}

	/* Draw channel names */
	draw_nlatree();

	curarea->win_swap= WIN_BACK_OK;
}

int count_nla_levels(void)
{
	Base *base;
	int y=0;

	for (y=0, base=G.scene->base.first; base; base=base->next)
	{
		if (nla_filter(base,0 )){
			/*	Ipo	*/
			y++;
			/*	Constraint channels */
			y+=BLI_countlist(&base->object->constraintChannels);

			if (base->object->type==OB_ARMATURE){
				/* Action */
				if(base->object->action){
				//	bActionChannel *achan;
					y++;

				//	for (achan=base->object->action->chanbase.first; achan; achan=achan->next){
				//		y+=BLI_countlist(&achan->constraintChannels);
				//	}
				}
				/* Nla strips */
				y+= BLI_countlist(&base->object->nlastrips);
			}
		}
	}

	return y;
}

int nla_filter (Base* base, int flags)
{
	Object *ob = base->object;

	/* Only objects with ipos */
	if (ob->ipo)
		return 1;

	if (ob->constraintChannels.first)
		return 1;

	/* Only armatures */
	if (ob->type==OB_ARMATURE)
		return 1;

	else return 0;
}

