// thingDoc.h : interface of the CThingDoc class
//
/////////////////////////////////////////////////////////////////////////////

#if !defined(AFX_THINGDOC_H__3640A73F_0D8A_47CE_8FA4_4CB1900EE513__INCLUDED_)
#define AFX_THINGDOC_H__3640A73F_0D8A_47CE_8FA4_4CB1900EE513__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include "film.h"
#include "camedit.h"

enum DataType
{
	DT_FLOAT,
	DT_INT,
	DT_NOTE,
	DT_LAST,
};

__inline char *int2note(int n)
{
	if (n==0) return "---";
	if (n<=0 || n>=127) return "";
	static char tmp[256];
	// middle c is 60
	char *notename[12]={"C","C#","D","D#","E","F","F#","G","G#","A","A#","B"};
	sprintf(tmp,"%s%d",notename[(n)%12],(n/12));
	return tmp;
}

__inline int note2int(const char *c)
{
	if (stricmp(c,"---")==0) return 0;
	if (stricmp(c,"===")==0) return 0;
	if (c[0]==0) return 0;

	int rv=atoi(c);
	if (rv) return rv;
	char n=toupper(c[0]);
	const char *ee=c+1;
	while (*ee && !iswspace(*ee)) ee++;
	char o[2]={ee[-1],0};	
	rv=12*atoi(o);	
	int notenum[7]={9,11,0,2,4,5,7};
	if (n>='A' && n<='G') rv+=notenum[n-'A']; else return 0;
	if (c[1]=='#' || c[1]=='+') rv++; else
	if (c[1]=='-') rv--; else
	if (toupper(c[1])=='B') rv--;
	return rv;
}


struct Data
{
	int time;
	union
	{
		float f;
		int i;
		char c[4];
		u8 u[4];
	};
	union
	{
		struct
		{
			bool ramp;
			char pad1,pad2,pad3;
		};
		int more;
	};
	

	Data(int xtime=0)
	{
		time=xtime;
		i=0;
		ramp=false;
	}

	void IncDec(int type, int amount=1,int alt=0)
	{
		switch (type)
		{
		case DT_FLOAT:
			f+=amount*0.1f;
			f=bound(0,f,1);
			break;
		case DT_INT:
			i+=amount;
			break;
		case DT_NOTE:
			if (alt) 
			{
				u[1]=bound(0,u[1]+amount,255);
			}
			else
			{
				u[0]=bound(0,u[0]+amount,255);
			}
			break;
		}
	}

	Data(float xtime, const Data &l, const Data &r, int type)
	{
		time=int(xtime);
		if (xtime>=r.time)
		{
			ramp=r.ramp;
			i=r.i;
			return;
		}
		if (r.ramp==false || xtime<=l.time)
		{
			ramp=l.ramp;
			i=l.i;
			return;
		}
		float frac;
		if (l.time>=r.time) frac=0; else frac = (xtime-l.time) / float(r.time-l.time);
		ramp=l.ramp;
		switch (type)
		{
		case DT_FLOAT:
			f=l.f + (r.f-l.f) * frac;
			break;
		case DT_INT:
			i=int(l.i + (r.i-l.i) * frac);
			break;
		case DT_NOTE:
			{
				float fracnote = l.u[0] + (r.u[0]-l.u[0]) * frac;			
				u[0]=int(fracnote);
				u[1]=int(l.u[1] + (r.u[1]-l.u[1]) * frac);
				u[2]=int((fracnote-u[0])*255.9f);
				break;
			}
		}
	}

	bool operator < (const Data &d) const
	{
		return time < d.time;
	}

	void Format(char *out, int type, int width=8) const
	{
		if (ramp) out[0]='>'; else out[0]='=';
		out++;
		width--;
		char fmt[16];
		switch (type)
		{
			case DT_FLOAT:
				sprintf(fmt,"%%%d.2f",width);
				sprintf(out,fmt,f);
				break;
			case DT_INT:
				sprintf(fmt,"%%%dd",width);
				sprintf(out,fmt,i);
				break;
			case DT_NOTE:
				if (u[1])
				{
					sprintf(fmt,"%%3s %%0%dd",width-4);					
				}
				else
				{
					sprintf(fmt,"%%3s");								
				}
				sprintf(out,fmt,int2note(u[0]),u[1]);
				break;
		}
	}

	int Scan(char *in, int type)
	{
		ramp=false;
		if (in[0]=='>' || in[0]=='<') 
		{
			ramp=true; 
			in++;
		} else if (in[0]=='=')
		{
			ramp=false;
			in++;
		}
		int ok=false;
		switch (type)
		{
		case DT_FLOAT:
			ok=sscanf(in,"%f",&f)==1;
			break;
		case DT_INT:
			ok=sscanf(in,"%d",&i)==1;
			break;
		case DT_NOTE:			
			while (iswspace(*in) && *in) in++;
			u[0]=note2int(in);			
			int j=0;
			if (strlen(in)>3) sscanf(in+3,"%d",&j);
			u[1]=j;
			u[2]=0;
			ok=true;
			break;			
		}
		return ok;
	}
};

typedef std::set<Data> DataSet;
typedef DataSet::iterator DataIt;

struct Channel
{
	char name[256];
	int type;
	int width;
	int x;

	Channel(char *n="", int t=DT_INT, int w=8)
	{
		Set(n,t,w);
		x=0;
		cur=data.end();
	}

	void Set(char *n="", int t=DT_INT, int w=8)
	{
		type=t;
		strcpy(name,n);
		width=w;
		name[width]=0;
	}

	DataSet data;
	DataIt cur;

	Data GetVal(float time, float *pretime=NULL, float *posttime=NULL)
	{
		if (pretime) *pretime=0;
		if (posttime) *posttime=10000;

		if (data.empty()) return Data(int(time));
		cur=data.begin();
		while (cur!=data.end() && cur->time < time) ++cur;
		if (cur==data.end()) --cur;
		while (cur!=data.begin() && cur->time > time) --cur;				
		//if (cur->time==time) return *cur;

		Data pre=Data(time);
		Data post=pre;
		if (cur->time > time) 
		{
			post=*cur;
		}
		else
		{
			pre=*cur;
			post=pre;
			DataIt next=cur;
			while (next!=data.end())
			{
				if (next->time>time) 
				{
					post=*next;
					break;
				}
				++next;			
			}						
		}
		if (pretime) *pretime=pre.time;
		if (posttime) *posttime=post.time;
		return Data(time, pre, post, type); 
	}

	void SetVal(const Data &d)
	{
		cur = data.find(d);		
		if (cur==data.end())
		{
			data.insert(d);
		}
		else
		{
			*((Data*)&*cur)=d;
		}
	}

	void InsertBlank(int time, int length)
	{
		for (DataIt i = data.begin(); i!=data.end(); ++i)
		{
			if (i->time >= time)
			{
				((Data*)&*i)->time+=length;
			}
		}
	}

	void Clear(int time, int length)
	{
		DataIt i2;
		for (DataIt i = data.begin(); i!=data.end(); i=i2)
		{
			i2=i;++i2;
			if (i->time>=time)
			{
				if (i->time<time+length) 
				{
					if (i==cur) cur=data.end();
					data.erase(i); 
				}				
			}
		}		
	}

	void Delete(int time, int length)
	{

		DataIt i2;
		for (DataIt i = data.begin(); i!=data.end(); i=i2)
		{
			i2=i;++i2;
			if (i->time>=time)
			{
				if (i->time<time+length) 
				{
					if (i==cur) cur=data.end();
					data.erase(i); 
				}
				else ((Data*)&*i)->time-=length;
			}
		}		
	}

};

typedef std::vector<Channel> ChannelList;

#define FRAMESPERROW 4

class CThingDoc : public CDocument
{
protected: // create from serialization only
	CThingDoc();
	DECLARE_DYNCREATE(CThingDoc)

// Attributes
public:
	int nonotes;
	int running;
	ChannelList channels;
	int maxtime;
	int scrollpos;
	int curchan;
	int curpos;
	int scrollx;
	int zoom;
	int selpos1,selpos2;
	int selchan1,selchan2;
	int numrows,lastrow;

	int playing;
	int playpos;
	int playpossub;
	float lasttime;

	float talkielevel;
	float pulseprob;
	int pulsetrigger;
	float animframe[2];
	float mvol;

	void StartPlaying(int from)
	{
		if (playing) StopPlaying();
		playpos=from;
		playpossub=0;
		lasttime=GetCurTime();
		playing=true;
	}

	void StopPlaying()
	{
		if (playing)
		{
			playing=false;
			lasttime=GetCurTime();
		}
	}

	
	float GetCurTime()
	{		
		if (playing) return (playpos*FRAMESPERROW*MBLURFAC+playpossub)/25.f/MBLURFAC;
				else return (curpos *FRAMESPERROW*MBLURFAC+0		 )/25.f/MBLURFAC;
		
	}

	void IncTime(int amount = MBLURFAC)
	{
		playpossub+=amount;
		if (playpossub>=FRAMESPERROW * MBLURFAC)
		{
			playpossub-=FRAMESPERROW * MBLURFAC;
			playpos++;
		}
	}

	int GetRow(int y)
	{	
		y=((y-8)/(8))*zoom+scrollpos;
		if (y<0) y=0;
		if (y>maxtime) y=maxtime;
		return y;
	}

	int GetChan(int x)
	{
		if (x<8*4) return 0;
		for (int c1=0;c1<(int)channels.size();c1++)
		{
			int xx=(channels[c1].width + channels[c1].x) * 8;
			if (x<xx) return c1;
		}
		return 0;
	}

	void CalcChanX()
	{
		int x=4-scrollx;
		for (int c1=0;c1<(int)channels.size();c1++)
		{
			channels[c1].x=x;
			x+=channels[c1].width;
		}
	}

// Operations
public:

// Overrides
	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CThingDoc)
	public:
	virtual BOOL OnNewDocument();
	virtual void Serialize(CArchive& ar);
	virtual BOOL CanCloseFrame(CFrameWnd* pFrame);
	//}}AFX_VIRTUAL

// Implementation
public:
	virtual ~CThingDoc();
#ifdef _DEBUG
	virtual void AssertValid() const;
	virtual void Dump(CDumpContext& dc) const;
#endif

protected:

// Generated message map functions
protected:
	//{{AFX_MSG(CThingDoc)
	afx_msg void OnUpdateDogo(CCmdUI* pCmdUI);
	afx_msg void OnDogo();
	afx_msg void OnDontgo();
	afx_msg void OnMakeFilm();
	afx_msg void OnUpdateDontgo(CCmdUI* pCmdUI);
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};

/////////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_THINGDOC_H__3640A73F_0D8A_47CE_8FA4_4CB1900EE513__INCLUDED_)
