//>>> Tux::HeaderView+ [BlueSonicBoy]
//this file is part of eMule
//Copyright (C)2002-2006 Merkur ( strEmail.Format("%s@%s", "devteam", "emule-project.net") / http://www.emule-project.net )
//
//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.
//
//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., 675 Mass Ave, Cambridge, MA 02139, USA.

#include "stdafx.h"
#include "eMule.h"
#include "HeaderDetailDialog.h"
#include "UserMsgs.h"
#include "PartFile.h"


#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

///////////////////////////////////////////////////////////////////////////////
// CHeaderDetailDialog dialog

LPCTSTR CHeaderDetailDialog::sm_pszNotAvail = _T("-");

IMPLEMENT_DYNAMIC(CHeaderDetailDialog, CResizablePage)

BEGIN_MESSAGE_MAP(CHeaderDetailDialog, CResizablePage)
	ON_MESSAGE(UM_DATA_CHANGED, OnDataChanged)
END_MESSAGE_MAP()

CHeaderDetailDialog::CHeaderDetailDialog()
: CResizablePage(CHeaderDetailDialog::IDD, 0)
{
	m_paFiles = NULL;
	m_bDataChanged = false;
	m_strCaption = GetResString(IDS_HEADERCAPTION);
	m_psp.pszTitle = m_strCaption;
	m_psp.dwFlags |= PSP_USETITLE;
}

CHeaderDetailDialog::~CHeaderDetailDialog()
{
	m_fontMono.DeleteObject();
}


void CHeaderDetailDialog::DoDataExchange(CDataExchange* pDX)
{
	CResizablePage::DoDataExchange(pDX);
}

BOOL CHeaderDetailDialog::OnInitDialog()
{
	CResizablePage::OnInitDialog();
	InitWindowStyles(this);

	AddAnchor(IDC_HEADERDUMP, TOP_LEFT, TOP_RIGHT);
	AddAnchor(IDC_FILEHEADER_GROUP, TOP_LEFT, TOP_RIGHT);		
	AddAnchor(IDC_HEADERCAPTION, TOP_LEFT, TOP_RIGHT);  
	AddAnchor(IDC_STATICFILEEXT, TOP_LEFT); 
	AddAnchor(IDC_EDIT2, TOP_LEFT);
	AddAnchor(IDC_STATICFILETYPE, TOP_LEFT);  
	AddAnchor(IDC_EDIT3, TOP_LEFT);
	AddAnchor(IDC_STATIC_ADD,  TOP_LEFT, TOP_RIGHT); 
	AddAnchor(IDC_STATIC_ADTXT, TOP_LEFT, TOP_RIGHT); 


	VERIFY( m_fontMono.CreatePointFont(100, _T("Courier New")) );//Courier New <- monospaced font, 10 point
	GetDlgItem(IDC_HEADERDUMP)->SetFont(&m_fontMono);

	Localize();

	return TRUE;
}

BOOL CHeaderDetailDialog::OnSetActive()
{
	if (!CResizablePage::OnSetActive())
		return FALSE;
	if (m_bDataChanged)
	{
		RefreshData();
		m_bDataChanged = false;
	}
	return TRUE;
}

class CSyncHelper
{
public:
	CSyncHelper()
	{
		m_pObject = NULL;
	}
	~CSyncHelper()
	{
		if (m_pObject)
			m_pObject->Unlock();
	}
	CSyncObject* m_pObject;
};

/* Much code adapted from void CUpDownClient::CreateNextBlockPackage() :o) */
void CHeaderDetailDialog::LoadHeaderData()
{ 
	CString Ext;
	CString fullname;

	//Get file and create a 'block'
	const CKnownFile* file = STATIC_DOWNCAST(CKnownFile, (*m_paFiles)[0]);

	Requested_Block_Struct* block = new Requested_Block_Struct;
	block->StartOffset = 0;
	block->EndOffset	= 255;

	//Get name
	fullname.Format(_T("%s"),file->GetFileName());
	//Get extension if there is one! Then display it. 
	if(fullname.ReverseFind(_T('.')) > fullname.GetLength()-6)
	{
		Ext = fullname.Right(4);
		if(Ext.Find(_T('.'))!= -1) Ext = fullname.Right(3); //fix for version 1.5f
		Ext.MakeUpper();
		GetDlgItem(IDC_EDIT2)->SetWindowText(Ext);
	} 

	//have we got some or all of the header?
	if(file->IsPartFile() && ((CPartFile*)file)->IsPureGap(block->StartOffset, block->EndOffset))
	{
		GetDlgItem(IDC_STATIC_ADTXT)->SetWindowText(GetResString(IDS_HDR_NOTDOWNLOADED));
		delete block;
		return;
	}

	CSyncHelper lockFile; 

	if(file->IsPartFile())// && ((CPartFile*)file)->GetStatus() != PS_COMPLETE)
	{ // Do not access a part file, if it is currently moved into the incoming directory.
		// Because the moving of part file into the incoming directory may take a noticable 
		// amount of time, we can not wait for 'm_FileCompleteMutex' and block the main thread.
		if(!((CPartFile*)file)->m_FileCompleteMutex.Lock(0))
		{
			GetDlgItem(IDC_STATIC_ADTXT)->SetWindowText(GetResString(IDS_HDR_FILECOMPLETING));
			delete block;
			return; // just do a quick test of the mutex's state and return if it's locked.
		}
		//else lock file mutex
		lockFile.m_pObject = &((CPartFile*)file)->m_FileCompleteMutex;
		// If it's a part file which we are uploading the file remains locked until we've read the
		// current block. This way the file completion thread can not (try to) "move" the file into
		// the incoming directory. 
		fullname = RemoveFileExtension(((CPartFile*)file)->GetFullName());
	}
	else
		fullname.Format(_T("%s\\%s"),file->GetPath(),file->GetFileName());

	CFile dfile;
	CString RealExt;
	CString Comment(GetResString(IDS_STATIC_ADTXT));
	CString Header;
	CString OneLine;	
	UINT total = 0; 
	//allocate a buffer
	byte* filedata = new byte[256+500];

	if(!file->IsPartFile())
	{
		if(!dfile.Open(fullname,CFile::modeRead|CFile::osSequentialScan|CFile::shareDenyNone))
		{
			//Add warn and exit
			GetDlgItem(IDC_STATIC_ADTXT)->SetWindowText(GetResString(IDS_FILEOPENERR));
			return;
		}
		dfile.Seek(block->StartOffset,0);
		if((total = dfile.Read(filedata,256)) != 256)
		{
			dfile.SeekToBegin();
			total += dfile.Read(filedata + total,256 - total);
		}
		dfile.Close();
	}
	else
	{
		CPartFile* partfile = (CPartFile*)file;
		partfile->m_hpartfile.Seek(block->StartOffset,0);
		if((total = partfile->m_hpartfile.Read(filedata,256)) != 256){
			partfile->m_hpartfile.SeekToBegin();
			total += partfile->m_hpartfile.Read(filedata + total,256 - total);
		}
	}
	if(lockFile.m_pObject)
	{
		lockFile.m_pObject->Unlock(); // Unlock the (part) file as soon as we are done with accessing it.
		lockFile.m_pObject = NULL;
	}

	if(total > 256)
		total = 256;

	for(uint16 x=0,i=0;i<total;) //(total - 1);)
	{
		//Create one display line
		OneLine.Format(_T(" %02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x  ")
			,filedata[i],filedata[i+1],filedata[i+2],filedata[i+3],filedata[i+4],filedata[i+5],filedata[i+6],filedata[i+7]
			,filedata[i+8],filedata[i+9],filedata[i+10],filedata[i+11],filedata[i+12],filedata[i+13],filedata[i+14],filedata[i+15]);
			//Make it upper case for spacing then add the ascII parts
			OneLine.MakeUpper();
			//char 1		
			for( x=i+16 ;i<x; i++)
			{
				if(filedata[i]>0x1f)
					OneLine += (TCHAR)((byte)filedata[i]);
				else
					OneLine += _T(" ");
			}
			Header += OneLine + _T("\r\n");
	}

	delete block;
	//display the header
	GetDlgItem(IDC_HEADERDUMP)->SetWindowText(Header);
	// CString Password("This archive is password protected! The password may be in the title, comments or available from a URL in the title.");

	if((filedata[0]==(byte)'M' && filedata[1]==(byte)'Z') || (filedata[0]==(byte)'Z' && filedata[1]==(byte)'M'))
		RealExt = _T("EXE");
	else if(filedata[0]==(byte)'R' && filedata[1]==(byte)'a' && filedata[2]==(byte)'r'  && filedata[3]==(byte)'!')
	{
		//set real extension and detect encryption /need for a password
		if(filedata[22]==0x74 && (filedata[10] & 0x04) && (filedata[23] & 0x4))
			Comment = GetResString(IDS_HDR_PASSWORDPROTECT);
		else if((filedata[10] & 0x04) && filedata[22]!=0x74)
			Comment = GetResString(IDS_HDR_LOCKATTRIBUTE);
		RealExt = _T("RAR");
	}
	else if(filedata[7]==(byte)'*' && filedata[8]==(byte)'*' && filedata[9]==(byte)'A' && filedata[10]==(byte)'C' && filedata[11]==(byte)'E' && filedata[12]==(byte)'*' && filedata[13]==(byte)'*')
	{
		RealExt = _T("ACE");
		if(filedata[26]==(byte)0x34 && filedata[27]==(byte)0x20) 
			Comment = GetResString(IDS_HDR_PASSWORDPROTECT);
	}
	else if(filedata[0]==(byte)0xff && filedata[1]==(byte)0xd8 && filedata[2]==(byte)0xff)
	{
		if(!Ext.Compare(_T("JPEG")))
			RealExt = Ext;
		else
			RealExt = _T("JPG");
	}
	else if(filedata[2]==(byte)0x00 && filedata[3]==(byte)0x0c && filedata[4]==(byte)0x6a && filedata[5]==(byte)0x50)
		RealExt = _T("JP2");
	else if(filedata[0]==(byte)'P' && filedata[1]==(byte)'K' && filedata[2]==(byte)0x03 && filedata[3]==(byte)0x04)
	{
		//set real extension and detect encryption /need for a password
		if(filedata[6] & 0x1)
			Comment = GetResString(IDS_HDR_PASSWORDPROTECT);
		RealExt = _T("ZIP");
	}
	else if(filedata[0]==(byte)0x1f && filedata[1]==(byte)0x8b && filedata[2]==(byte)0x08)
		RealExt = _T(".GZ");
	else if(filedata[0]==(byte)0x4f && filedata[1]==(byte)0x67 && filedata[2]==(byte)0x67 && filedata[3]==(byte)0x53) //The good
	{
		if(!Ext.Compare(_T("OGG")))
			RealExt = Ext;
		else
			RealExt = _T("OGM");
	}
	else if(filedata[0]==(byte)0x30 && filedata[1]==(byte)0x26 && filedata[2]==(byte)0xb2 && filedata[3]==(byte)0x75 && filedata[4]==(byte)0x8e && filedata[5]==(byte)0x66 && filedata[6]==(byte)0xcf && filedata[7]==(byte)0x11 && filedata[8]==(byte)0xa6 && filedata[9]==(byte)0xd9) //the bad AND the ugly!!! >:o) 9 characters is enought to ID these ~*nasty*~ file formats
	{
		if(!Ext.Compare(_T("WMV")))
			RealExt = Ext;
		else
			RealExt = _T("WMA");
		Comment = GetResString(IDS_HDR_DRMWARNING);
	}
	else if(filedata[0]==(byte)0x4d && filedata[1]==(byte)0x53 && filedata[2]==(byte)0x43 && filedata[3]==(byte)0x46 && filedata[4]==(byte)0x0 && filedata[5]==(byte)0x0 && filedata[6]==(byte)0x0 && filedata[7]==(byte)0x0)
	{
		RealExt = _T("CAB");
		Comment = GetResString(IDS_HDR_MSCAB);
	}
	else if(filedata[0]==(byte)0x46 && filedata[1]==(byte)0x49 && filedata[2]==(byte)0x4c && filedata[3]==(byte)0x45 && filedata[4]==(byte)0x20 && filedata[5]==(byte)0x22) //CDRWin CUE
		RealExt = _T("CUE");
	else if(filedata[0]==(byte)0x49 && filedata[1]==(byte)0x53 && filedata[2]==(byte)0x63 && filedata[3]==(byte)0x28)
	{
		RealExt = _T("CAB");
		Comment = GetResString(IDS_HDR_INSHCAB);
	}
	else if(filedata[0]==(byte)0xd0 && filedata[1]==(byte)0xcf && filedata[2]==(byte)0x11 && filedata[3]==(byte)0xe0 && filedata[4]==(byte)0xa1 && filedata[5]==(byte)0xb1 && filedata[6]==(byte)0x1a && filedata[7]==(byte)0xe1) //MS-Word DOC
		RealExt = _T("DOC");
	else if((filedata[0]==(byte)0x4d && filedata[1]==(byte)0x4d && filedata[2]==(byte)0x00 && filedata[3]==(byte)0x2a) || (filedata[0]==(byte)0x49 && filedata[1]==(byte)0x49 && filedata[2]==(byte)0x2a && filedata[3]==(byte)0x00)) //big and little endian TIFF headers
	{
		if(!Ext.Compare(_T("TIFF")))
			RealExt = Ext;
		else
			RealExt = _T("TIF");
	}
	else if(filedata[0]==(byte)0x37 && filedata[1]==(byte)0x7A && filedata[2]==(byte)0xbc && filedata[3]==(byte)0xaf && filedata[4]==(byte)0x27 && filedata[5]==(byte)0x1c)
		RealExt = _T(".7Z");
	else if(filedata[0]==(byte)0x7b && filedata[1]==(byte)0x5c && filedata[2]==(byte)'r' && filedata[3]==(byte)'t' && filedata[4]==(byte)'f')
		RealExt = _T("RTF");
	else if(filedata[0]==(byte)'%' && filedata[1]==(byte)'P' && filedata[2]==(byte)'D' && filedata[3]==(byte)'F')
		RealExt = _T("PDF");
	else if(filedata[0]==(byte)'R' && filedata[1]==(byte)'I' && filedata[2]==(byte)'F' && filedata[3]==(byte)'F')
	{
		//Various RIFF formats 
		if(filedata[8]==(byte)'A' && filedata[9]==(byte)'V' && filedata[10]==(byte)'I')
			RealExt = _T("AVI");
		else if(filedata[8]==(byte)'W' && filedata[9]==(byte)'A' && filedata[10]==(byte)'V')
			RealExt = _T("WAV");
	}
	else if(filedata[0]==(byte)'.' && filedata[1]==(byte)'R' && filedata[2]==(byte)'M' && filedata[3]==(byte)'F')
		RealExt = _T(".RM");
	else if(filedata[0]==(byte)0x00 && filedata[1]==(byte)0x00 && filedata[2]==(byte)0x01 && filedata[3]==(byte)0xba)
	{
		//Fix for VOB files in version 2.0b
		if(!Ext.Compare(_T("MPEG")) || !Ext.Compare(_T("VOB")))
			RealExt = Ext;
		else
			RealExt = _T("MPG"); //maybe mpeg
	}
	else if(filedata[0]==(byte)'G' && filedata[1]==(byte)'I' && filedata[2]==(byte)'F' && filedata[3]==(byte)'8')
		RealExt = _T("GIF");
	else if(filedata[0]==(byte)0x89 && filedata[1]==(byte)0x50 && filedata[2]==(byte)0x4e && filedata[3]==(byte)0x47)
		RealExt = _T("PNG");
	else if(filedata[0]==(byte)0x4d && filedata[1]==(byte)0x54 && filedata[2]==(byte)0x68 && filedata[3]==(byte)0x64 && filedata[4]==(byte)0x0 && filedata[5]==(byte)0x0 && filedata[6]==(byte)0x0 && filedata[7]==(byte)0x06 && filedata[8]==(byte)0x0 && filedata[9]==(byte)0x01)
		RealExt = _T("MID");
	else if((filedata[0]==(byte)'S' && filedata[1]==(byte)'t' && filedata[2]==(byte)'u' && filedata[3]==(byte)'f' && filedata[4]==(byte)'f' && filedata[5]==(byte)'I' && filedata[6]==(byte)'t') || (filedata[0]==(byte)'S' && filedata[1]==(byte)'I' && filedata[2]==(byte)'T' && filedata[3]==(byte)'!')) //mac StuffIt sit files
		RealExt = _T("SIT");
	else if(filedata[4]==(byte)0x66 && filedata[5]==(byte)0x74 && filedata[6]==(byte)0x79 && filedata[7]==(byte)0x70)
	{	
		if(!Ext.Compare(_T("MOV"))) //May have other extentions such as MOV related formats.....
			RealExt = _T("MOV");
		else
			RealExt = _T("MP4");
	}
	else if(filedata[4]==(byte)0x6d && filedata[5]==(byte)0x6f && filedata[6]==(byte)0x6f && filedata[7]==(byte)0x76)
		RealExt = _T("MOV");
	else if((filedata[0]==(byte)0xff && (filedata[1]==(byte)0xfb || filedata[1]==(byte)0xfa)) || (filedata[0]==(byte)'I' && filedata[1]==(byte)'D' && filedata[2]==(byte)'3'))
		RealExt = _T("MP3");
	else if(filedata[2]==(byte)0x2d && filedata[3]==(byte)0x6C && filedata[4]==(byte)0x68)
		RealExt = _T("LZH");
	else if(filedata[0]==(byte)'I' && filedata[1]==(byte)'T' && filedata[2]==(byte)'S' && filedata[3]==(byte)'F')
		RealExt = _T("CHM");
	else if(filedata[0]==(byte)'B' && filedata[1]==(byte)'M')
		RealExt = _T("BMP");
	//Below are *confirmations* only!
	else if((filedata[0]==(byte)'x' || (filedata[0]==(byte)'E' && filedata[1]==(byte)'R')) && !Ext.Compare(_T("DMG")))
		RealExt = _T("DMG"); //confirm only!!
	else if(filedata[1]==(byte)0x0 && !Ext.Compare(_T("TGA")))
		RealExt = _T("TGA"); //confirm only!!
	else if(filedata[0]==(byte)0xff && filedata[1]==(byte)0xfe && !Ext.Compare(_T("REG")))
		RealExt = _T("REG"); //confirm only!!

	//Set the *real* extension if we know it!
	GetDlgItem(IDC_EDIT3)->SetWindowText(RealExt);

	//Comment on dissimilar extension 
	if(Ext.Compare(RealExt) && !RealExt.IsEmpty())
	{
		if(!Comment.Compare(CString(GetResString(IDS_STATIC_ADTXT))))
			Comment.Empty();
		else
			Comment += _T(" ");

		Comment += GetResString(IDS_HDR_EXTMISMATCH);
		if(file->IsPartFile() && ((CPartFile*)file)->DissimilarName())
			Comment += GetResString(IDS_HDR_FDC_MULTINAME);	//>>> Tux::FDC [BlueSonicBoy]: it's not exactly an in your face disparity alert eg: when you disable FDC you get rid of the red ? No harm in pointing out there are different names here!
	}

	GetDlgItem(IDC_STATIC_ADTXT)->SetWindowText(Comment);

	delete[] filedata;
	return;
}



LRESULT CHeaderDetailDialog::OnDataChanged(WPARAM, LPARAM)
{
	m_bDataChanged = true;
	return 1;
}

void CHeaderDetailDialog::RefreshData()
{
	//reset the data fields which start out blank
	GetDlgItem(IDC_EDIT2)->SetWindowText(_T(""));//file extension
	GetDlgItem(IDC_EDIT3)->SetWindowText(_T(""));//real extension
	GetDlgItem(IDC_HEADERDUMP)->SetWindowText(_T(""));//header
	LoadHeaderData();
}

void CHeaderDetailDialog::Localize()
{
	GetDlgItem(IDC_FILEHEADER_GROUP )->SetWindowText(GetResString(IDS_FILEHEADER_GROUP));
	GetDlgItem(IDC_HEADERCAPTION)->SetWindowText(GetResString(IDS_HEADERCAPTION));
	GetDlgItem(IDC_STATICFILEEXT)->SetWindowText(GetResString(IDS_STATICFILEEXT));
	GetDlgItem(IDC_STATICFILETYPE)->SetWindowText(GetResString(IDS_STATICFILETYPE));
	GetDlgItem(IDC_STATIC_ADD)->SetWindowText(GetResString(IDS_STATIC_ADD));
}
//<<< Tux::HeaderView+ [BlueSonicBoy]