#include"common.h"

#include<iostream>
#include<fstream>



extern HWND hMainWindow;

extern Data main_data;


namespace Place
{
	extern Module NewModule;
}

namespace FileIO
{
	extern char szFile[MAX_PATH];
	extern char szFileTitle[MAX_PATH];
}


std::istream &operator>>(std::istream &stream,vector2D &v)
{
	stream >> v.x >> v.y;
	return stream;
}

std::ostream &operator<<(std::ostream &stream,vector2D v)
{
	stream << v.x << " " << v.y << " ";
	return stream;
}

std::istream &operator>>(std::istream &stream,Text &l)
{
	stream >> l.pos;
	stream.get();
	stream.getline(l.txt,SIZE_OF_LABEL_NAME);
	return stream;
}

std::ostream &operator<<(std::ostream &stream,Text l)
{
	stream << l.pos;
	stream << l.txt;
	stream << "\n";
	return stream;
}

std::istream &operator>>(std::istream &stream,Label &l)
{
	stream >> (int&)l.d;
	stream >> l.node_index;
	stream.get();
	stream.getline(l.nam,SIZE_OF_LABEL_NAME);
	return stream;
}

std::ostream &operator<<(std::ostream &stream,Label l)
{
	stream << l.d << " ";
	stream << l.node_index << " ";
	stream << l.nam;
	stream << "\n";
	return stream;
}

std::istream &operator>>(std::istream &stream,LabelIn &l)
{
	stream >> l.state;
	stream >> (Label&)l;
	return stream;
}

std::ostream &operator<<(std::ostream &stream,LabelIn l)
{
	stream << l.state << " ";
	stream << (Label)l;
	return stream;
}

std::istream &operator>>(std::istream &stream,DeviceIndex &sd)
{
	stream >> (int&)sd.ConnectedDevClass;
	stream >> sd.Index;
	stream >> sd.SubIndex;
	return stream;
}

std::ostream &operator<<(std::ostream &stream,DeviceIndex sd)
{
	stream << sd.ConnectedDevClass << " ";
	stream << sd.Index << " ";
	stream << sd.SubIndex << " ";
	return stream;
}

std::istream &operator>>(std::istream &stream,LabelOut &l)
{
	stream >> l.sd;
	stream >> (Label&)l;
	return stream;
}

std::ostream &operator<<(std::ostream &stream,LabelOut l)
{
	stream << l.sd;
	stream << (Label)l;
	return stream;
}

std::istream &operator>>(std::istream &stream,Gate &g)
{
	stream >> g.state;
	stream >> (int&)g.op;
	stream >> (int&)g.d;
	stream >> g.node_out;
	unsigned int size;
	stream >> size;
	if(!stream.good()) return stream;
	for(unsigned int i=0;i<size;i++)
	{
		SourceData gin;
		stream >> gin;
		g.array_in.push_back(gin);
	}
	return stream;
}

std::ostream &operator<<(std::ostream &stream,Gate g)
{
	stream << g.state << " ";
	stream << g.op << " ";
	stream << g.d << "  ";
	stream << g.node_out << "  ";
	stream << g.array_in.size() << "  ";
	for(unsigned int i=0;i<g.array_in.size();i++) stream << g.array_in[i] << " ";
	stream << "\n";
	return stream;
}

std::istream &operator>>(std::istream &stream,Conductor &c)
{
	stream >> c.Multipath;
	stream >> c.Short;
	stream >> (int&)c.d;
	stream >> c.nod[0];
	stream >> c.nod[1];
	return stream;
}

std::ostream &operator<<(std::ostream &stream,Conductor c)
{
	stream << c.Multipath << " ";
	stream << c.Short << " ";
	stream << c.d << " ";
	stream << c.nod[0] << " ";
	stream << c.nod[1] << " ";
	stream << "\n";
	return stream;
}

std::istream &operator>>(std::istream &stream,Node &n)
{
	stream >> n.pos;
	stream >> n.Multipath;
	stream >> n.Short;
	stream >> n.AllRight;
	for(int i=0;i<8;i++) stream >> n.branch[i];
	return stream;
}

std::ostream &operator<<(std::ostream &stream,Node n)
{
	stream << n.pos << " ";
	stream << n.Multipath << " ";
	stream << n.Short << " ";
	stream << n.AllRight << "  ";
	for(int i=0;i<8;i++) stream << n.branch[i];
	stream << "\n";
	return stream;
}

std::istream &operator>>(std::istream &stream,SourceData &in)
{
	stream >> in.node_in;
	stream >> in.sd;
	return stream;
}

std::ostream &operator<<(std::ostream &stream,SourceData in)
{
	stream << in.node_in << " ";
	stream << in.sd;
	return stream;
}



std::istream &operator>>(std::istream &stream,Module &m)
{
	unsigned int size;

	stream >> m.center;
	stream >> m.internal_half_width;
	stream >> m.internal_half_height;
	stream >> m.up_margin;
	stream >> m.right_margin;
	stream >> m.down_margin;
	stream >> m.left_margin;

	stream >> size;
	if(!stream.good()) return stream;
	for(unsigned int i=0;i<size;i++)
	{
		DeviceIndex di;
		stream >> di;
		m.up.push_back(di);
	}

	stream >> size;
	if(!stream.good()) return stream;
	for(unsigned int i=0;i<size;i++)
	{
		DeviceIndex di;
		stream >> di;
		m.right.push_back(di);
	}

	stream >> size;
	if(!stream.good()) return stream;
	for(unsigned int i=0;i<size;i++)
	{
		DeviceIndex di;
		stream >> di;
		m.down.push_back(di);
	}

	stream >> size;
	if(!stream.good()) return stream;
	for(unsigned int i=0;i<size;i++)
	{
		DeviceIndex di;
		stream >> di;
		m.left.push_back(di);
	}

	stream >> size;
	if(!stream.good()) return stream;
	for(unsigned int i=0;i<size;i++)
	{
		int j;
		stream >> j;
		m.node_out.push_back(j);
	}

	stream >> size;
	if(!stream.good()) return stream;
	for(unsigned int i=0;i<size;i++)
	{
		SourceData in;
		stream >> in;
		m.array_in.push_back(in);
	}

	stream >> (Data&)m;

	return stream;
}

std::ostream &operator<<(std::ostream &stream,Module m)
{
	stream << m.center << "  ";
	stream << m.internal_half_width << " ";
	stream << m.internal_half_height << " ";
	stream << m.up_margin << " ";
	stream << m.right_margin << " ";
	stream << m.down_margin << " ";
	stream << m.left_margin << " ";
	stream << "\n";

	stream << m.up.size() << "\n";
	for(unsigned int i=0;i<m.up.size();i++) stream << m.up[i];
	stream << "\n";
	stream << m.right.size() << "\n";
	for(unsigned int i=0;i<m.right.size();i++) stream << m.right[i];
	stream << "\n";
	stream << m.down.size() << "\n";
	for(unsigned int i=0;i<m.down.size();i++) stream << m.down[i];
	stream << "\n";
	stream << m.left.size() << "\n";
	for(unsigned int i=0;i<m.left.size();i++) stream << m.left[i];
	stream << "\n";

	stream << m.node_out.size() << "\n";
	for(unsigned int i=0;i<m.node_out.size();i++) stream << m.node_out[i] << " ";
	stream << "\n\n";
	stream << m.array_in.size() << "\n";
	for(unsigned int i=0;i<m.array_in.size();i++) stream << m.array_in[i] << " ";
	stream << "\n\n";

	stream << (Data)m;

	return stream;

}

std::istream &operator>>(std::istream &stream,Data &d)
{
	int size;

	stream >> size;
	if(!stream.good()) return stream;
	for(int i=0;i<size;i++)
	{
		LabelIn l(NO_DIRECTION,-1,"");
		stream >> l;
		d.input.push_back(l);
	}

	stream >> size;
	if(!stream.good()) return stream;
	for(int i=0;i<size;i++)
	{
		LabelOut l(NO_DIRECTION,-1,"");
		stream >> l;
		d.output.push_back(l);
	}

	stream >> size;
	if(!stream.good()) return stream;
	for(int i=0;i<size;i++)
	{
		std::vector<SourceData> v;
		Gate g(NOT,NO_DIRECTION,DirectionVector[NO_DIRECTION],-1,v);
		stream >> g;
		d.gate.push_back(g);
	}

	stream >> size;
	if(!stream.good()) return stream;
	for(int i=0;i<size;i++)
	{
		Conductor c(-1,-1,NO_DIRECTION);
		stream >> c;
		d.conductor.push_back(c);
	}

	stream >> size;
	if(!stream.good()) return stream;
	for(int i=0;i<size;i++)
	{
		Node n;
		stream >> n;
		d.node_data.push_back(n);
	}

	stream >> size;
	if(!stream.good()) return stream;
	for(int i=0;i<size;i++)
	{
		Module m;
		stream >> m;
		d.module.push_back(m);
	}

	stream >> size;
	if(!stream.good()) return stream;
	for(int i=0;i<size;i++)
	{
		Text t(DirectionVector[NO_DIRECTION],"");
		stream >> t;
		d.note.push_back(t);
	}

	return stream;
}

std::ostream &operator<<(std::ostream &stream,Data d)
{
	stream << d.input.size() <<"\n\n";
	for(unsigned int i=0;i<d.input.size();i++) stream << d.input[i];
	stream << "\n\n";
	stream << d.output.size() << "\n\n";
	for(unsigned int i=0;i<d.output.size();i++) stream << d.output[i];
	stream << "\n\n";
	stream << d.gate.size() << "\n\n";
	for(unsigned int i=0;i<d.gate.size();i++) stream << d.gate[i];
	stream << "\n\n";
	stream << d.conductor.size() << "\n\n";
	for(unsigned int i=0;i<d.conductor.size();i++) stream << d.conductor[i];
	stream << "\n\n";
	stream << d.node_data.size() << "\n\n";
	for(unsigned int i=0;i<d.node_data.size();i++) stream << d.node_data[i];
	stream << "\n\n";
	stream << d.module.size() << "\n\n";
	for(unsigned int i=0;i<d.module.size();i++) stream << d.module[i];
	stream << "\n\n";
	stream << d.note.size() << "\n\n";
	for(unsigned int i=0;i<d.note.size();i++) stream << d.note[i];
	stream << "\n\n";
	return stream;
}

bool SaveAs(Data d,char *szF,char *szFT)
{
	OPENFILENAME ofn;

	memset(&ofn,0,sizeof(OPENFILENAME));
	ofn.lStructSize = sizeof(OPENFILENAME);
	ofn.hwndOwner = hMainWindow;
	ofn.lpstrFilter = TEXT("log(*.log)\0*.log\0\0");
	ofn.lpstrFile = szF;
	ofn.lpstrFileTitle = szFT;
	ofn.nFilterIndex = 1;
	ofn.nMaxFile = MAX_PATH;
	ofn.nMaxFileTitle = MAX_PATH;
	ofn.Flags = OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY;
	ofn.lpstrDefExt = TEXT("log");
	ofn.lpstrTitle = TEXT("名前を付けて保存する");

	if(GetSaveFileName(&ofn) == 0) return false;

	std::ofstream f(szF);
	f << Version << std::endl << std::endl;
	f << d;
	f.close();
	return true;
}

bool Save()
{
	if(strlen(FileIO::szFile)==0) return SaveAs(main_data,FileIO::szFile,FileIO::szFileTitle);
	else
	{
		std::ofstream f(FileIO::szFile);
		if(f)
		{
			f << Version << std::endl << std::endl;
			f << main_data;
			f.close();
		}
		return true;
	}
}

bool LoadData()
{
	std::ifstream f(FileIO::szFile);
	if(f)
	{
		char s[SIZE_OF_LABEL_NAME];
		f.getline(s,SIZE_OF_LABEL_NAME);
		if(strcmp(s,Version)==0)
		{
			main_data.Clear();
			f >> main_data;
			if(!f.good())
			{
				main_data.Clear();
				FileIO::szFile[0]=0;
				FileIO::szFileTitle[0]=0;
				MessageBox(0,"ファイルが壊れています","エラー",MB_OK);
				return false;
			}
		}
		else
		{
			main_data.Clear();
			FileIO::szFile[0]=0;
			FileIO::szFileTitle[0]=0;
			MessageBox(0,"ファイルフォーマットの異なるファイルです","エラー",MB_OK);
			return false;
		}
		f.close();
	}
	return true;
}

bool Open()
{
	OPENFILENAME ofn;

	memset(&ofn,0,sizeof(OPENFILENAME));
	ofn.lStructSize = sizeof(OPENFILENAME);
	ofn.hwndOwner = hMainWindow;
	ofn.lpstrFilter = TEXT("log(*.log)\0*.log\0\0");
	ofn.lpstrFile = FileIO::szFile;
	ofn.lpstrFileTitle = FileIO::szFileTitle;
	ofn.nMaxFile = MAX_PATH;
	ofn.nMaxFileTitle = MAX_PATH;
	ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
	ofn.lpstrDefExt = TEXT("log");
	ofn.lpstrTitle = TEXT("ファイルを開く");

	if(GetOpenFileName(&ofn) == 0) return false;

	return LoadData();
}

void Module::AddLeft(DeviceIndex di)
{
	left.push_back(di);
	for(int i=left.size()-1;i>0;i--)
	{
		int c;
		vector2D v1,v2;
		v1=GetNodeCoordinate(left[i].GetDeviceClass()==module_in ? input[left[i].GetDeviceIndex()].nodeindex() : output[left[i].GetDeviceIndex()].nodeindex());
		v2=GetNodeCoordinate(left[i-1].GetDeviceClass()==module_in ? input[left[i-1].GetDeviceIndex()].nodeindex() : output[left[i-1].GetDeviceIndex()].nodeindex());
		c=CompareY(v1,v2);
		if(c>0) break;
		else if(c==0)
		{
			c=CompareX(v1,v2);
			if(c>0)break;
		}
		DeviceIndex tmp;
		tmp=left[i];
		left[i]=left[i-1];
		left[i-1]=tmp;
	}
}

void Module::AddUp(DeviceIndex di)
{
	up.push_back(di);
	for(int i=up.size()-1;i>0;i--)
	{
		int c;
		vector2D v1,v2;
		v1=GetNodeCoordinate(up[i].GetDeviceClass()==module_in ? input[up[i].GetDeviceIndex()].nodeindex() : output[up[i].GetDeviceIndex()].nodeindex());
		v2=GetNodeCoordinate(up[i-1].GetDeviceClass()==module_in ? input[up[i-1].GetDeviceIndex()].nodeindex() : output[up[i-1].GetDeviceIndex()].nodeindex());
		c=CompareX(v1,v2);
		if(c<0) break;
		else if(c==0)
		{
			c=CompareY(v1,v2);
			if(c>0)break;
		}
		DeviceIndex tmp;
		tmp=up[i];
		up[i]=up[i-1];
		up[i-1]=tmp;
	}
}

void Module::AddRight(DeviceIndex di)
{
	right.push_back(di);
	for(int i=right.size()-1;i>0;i--)
	{
		int c;
		vector2D v1,v2;
		v1=GetNodeCoordinate(right[i].GetDeviceClass()==module_in ? input[right[i].GetDeviceIndex()].nodeindex() : output[right[i].GetDeviceIndex()].nodeindex());
		v2=GetNodeCoordinate(right[i-1].GetDeviceClass()==module_in ? input[right[i-1].GetDeviceIndex()].nodeindex() : output[right[i-1].GetDeviceIndex()].nodeindex());
		c=CompareY(v1,v2);
		if(c<0) break;
		else if(c==0)
		{
			c=CompareX(v1,v2);
			if(c<0)break;
		}
		DeviceIndex tmp;
		tmp=right[i];
		right[i]=right[i-1];
		right[i-1]=tmp;
	}
}

void Module::AddDown(DeviceIndex di)
{
	down.push_back(di);
	for(int i=down.size()-1;i>0;i--)
	{
		int c;
		vector2D v1,v2;
		v1=GetNodeCoordinate(down[i].GetDeviceClass()==module_in ? input[down[i].GetDeviceIndex()].nodeindex() : output[down[i].GetDeviceIndex()].nodeindex());
		v2=GetNodeCoordinate(down[i-1].GetDeviceClass()==module_in ? input[down[i-1].GetDeviceIndex()].nodeindex() : output[down[i-1].GetDeviceIndex()].nodeindex());
		c=CompareX(v1,v2);
		if(c>0) break;
		else if(c==0)
		{
			c=CompareY(v1,v2);
			if(c<0)break;
		}
		DeviceIndex tmp;
		tmp=down[i];
		down[i]=down[i-1];
		down[i-1]=tmp;
	}
}

bool LoadModule()
{
	char szFile_Modul[MAX_PATH]="";
	char szFileTitle_Modul[MAX_PATH]="";
	OPENFILENAME ofn;

	memset(&ofn,0,sizeof(OPENFILENAME));
	ofn.lStructSize = sizeof(OPENFILENAME);
	ofn.hwndOwner = hMainWindow;
	ofn.lpstrFilter = TEXT("log(*.log)\0*.log\0\0");
	ofn.lpstrFile = szFile_Modul;
	ofn.lpstrFileTitle = szFileTitle_Modul;
	ofn.nMaxFile = MAX_PATH;
	ofn.nMaxFileTitle = MAX_PATH;
	ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
	ofn.lpstrDefExt = TEXT("log");
	ofn.lpstrTitle = TEXT("モジュールをロード");

	if(GetOpenFileName(&ofn) == 0) return false;

	std::ifstream f(szFile_Modul);
	if(!f)return false;
	
	char s[SIZE_OF_LABEL_NAME];
	f.getline(s,SIZE_OF_LABEL_NAME);
	if(strcmp(s,Version)==0)
	{
		Place::NewModule.Clear();
		f >> (Data&)Place::NewModule;
		if(!f.good())
		{
			Place::NewModule.Clear();
			MessageBox(0,"ファイルが壊れています","エラー",MB_OK);
			return false;
		}
	}
	else
	{
		MessageBox(0,"バージョンの異なるファイルです","エラー",MB_OK);
		return false;
	}
	f.close();

	if(Place::NewModule.nInput()==0 || Place::NewModule.nOutput()==0)
	{
		Place::NewModule.Clear();
		MessageBox(0,"入力端子と出力端子がそれぞれ少なくとも1つ以上必要です","エラー",MB_OK);
		return false;
	}

	DeviceIndex di;

	di.SetDeviceClass(module_in);
	for(unsigned int i=0;i<Place::NewModule.nInput();i++)
	{
		di.SetIndexAndSubIndex(i,-1);
		switch(Place::NewModule.GetInputDirection(i))
		{
		case RIGHT:
			Place::NewModule.AddLeft(di);
			break;
		case DOWN:
			Place::NewModule.AddUp(di);
			break;
		case LEFT:
			Place::NewModule.AddRight(di);
			break;
		case UP:
			Place::NewModule.AddDown(di);
			break;
		}
	}

	di.SetDeviceClass(module_out);
	for(unsigned int i=0;i<Place::NewModule.nOutput();i++)
	{
		di.SetIndexAndSubIndex(i,-1);
		switch(Place::NewModule.GetOutputDirection(i))
		{
		case RIGHT:
			Place::NewModule.AddLeft(di);
			break;
		case DOWN:
			Place::NewModule.AddUp(di);
			break;
		case LEFT:
			Place::NewModule.AddRight(di);
			break;
		case UP:
			Place::NewModule.AddDown(di);
			break;
		}
	}

	Place::NewModule.Set_internal_half_length(Place::NewModule.GetUpSize()>Place::NewModule.GetDownSize() ? Place::NewModule.GetUpSize() : Place::NewModule.GetDownSize(),Place::NewModule.GetRightSize()>Place::NewModule.GetLeftSize() ? Place::NewModule.GetRightSize() : Place::NewModule.GetLeftSize());

	unsigned int up=0;
	for(int i=0;i<Place::NewModule.GetUpSize();i++)
	{
		switch(Place::NewModule.GetUp(i).GetDeviceClass())
		{
		case module_in:
			if(up<strlen(Place::NewModule.GetInput(Place::NewModule.GetUp(i).GetDeviceIndex()).name())) up=strlen(Place::NewModule.GetInput(Place::NewModule.GetUp(i).GetDeviceIndex()).name());
			break;
		case module_out:
			if(up<strlen(Place::NewModule.GetOutput(Place::NewModule.GetUp(i).GetDeviceIndex()).name())) up=strlen(Place::NewModule.GetOutput(Place::NewModule.GetUp(i).GetDeviceIndex()).name());
			break;
		}
	}

	unsigned int right=0;
	for(int i=0;i<Place::NewModule.GetRightSize();i++)
	{
		switch(Place::NewModule.GetRight(i).GetDeviceClass())
		{
		case module_in:
			if(right<strlen(Place::NewModule.GetInput(Place::NewModule.GetRight(i).GetDeviceIndex()).name())) right=strlen(Place::NewModule.GetInput(Place::NewModule.GetRight(i).GetDeviceIndex()).name());
			break;
		case module_out:
			if(right<strlen(Place::NewModule.GetOutput(Place::NewModule.GetRight(i).GetDeviceIndex()).name())) right=strlen(Place::NewModule.GetOutput(Place::NewModule.GetRight(i).GetDeviceIndex()).name());
			break;
		}
	}

	unsigned int down=0;
	for(int i=0;i<Place::NewModule.GetDownSize();i++)
	{
		switch(Place::NewModule.GetDown(i).GetDeviceClass())
		{
		case module_in:
			if(down<strlen(Place::NewModule.GetInput(Place::NewModule.GetDown(i).GetDeviceIndex()).name())) down=strlen(Place::NewModule.GetInput(Place::NewModule.GetDown(i).GetDeviceIndex()).name());
			break;
		case module_out:
			if(down<strlen(Place::NewModule.GetOutput(Place::NewModule.GetDown(i).GetDeviceIndex()).name())) down=strlen(Place::NewModule.GetOutput(Place::NewModule.GetDown(i).GetDeviceIndex()).name());
			break;
		}
	}

	unsigned int left=0;
	for(int i=0;i<Place::NewModule.GetLeftSize();i++)
	{
		switch(Place::NewModule.GetLeft(i).GetDeviceClass())
		{
		case module_in:
			if(left<strlen(Place::NewModule.GetInput(Place::NewModule.GetLeft(i).GetDeviceIndex()).name())) left=strlen(Place::NewModule.GetInput(Place::NewModule.GetLeft(i).GetDeviceIndex()).name());
			break;
		case module_out:
			if(left<strlen(Place::NewModule.GetOutput(Place::NewModule.GetLeft(i).GetDeviceIndex()).name())) left=strlen(Place::NewModule.GetOutput(Place::NewModule.GetLeft(i).GetDeviceIndex()).name());
			break;
		}
	}

	Place::NewModule.Set_margin((up+1)/2,(right+1)/2,(down+1)/2,(left+1)/2);
	return true;
}