//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())
	   {
         //TK4 Mod v2.0f - C&P error had throw without try or catch! BUG fixed :/
		 if(!dfile.Open(fullname,CFile::modeRead|CFile::osSequentialScan|CFile::shareDenyNone))
		   { //Add warn and exit
			 GetDlgItem(IDC_STATIC_ADTXT)->SetWindowText(_T("File failed to open, moved or deleted?"));   
			 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   //The good
   if(filedata[0]==(byte)0x4f && filedata[1]==(byte)0x67 && filedata[2]==(byte)0x67 && filedata[3]==(byte)0x53)
    {
		//Bug fix version ver. 2.0f
	   if(!Ext.Compare(_T("OGG"))) RealExt = Ext;
	    else                       RealExt = _T("OGM"); 
	 }
    else  //the bad AND the ugly!!! >:o) 9 characters is enought to ID these ~*nasty*~ file formats
   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)
 	{  //Bug fix version ver. 2.0f
	   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 //CDRWin CUE                                              
   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) 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//MS-Word DOC
   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) RealExt = _T("DOC");
    else//big and little endian TIFF headers
   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))
	{
	  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 //mac StuffIt sit files
   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)'!')) 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"))) RealExt = _T("MOV");//May have other extentions such as MOV related formats.....
		  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");
    else //Below are *confirmations* only! Improved for 2.0f Mac ISOs canc also start with ER
   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);//[FDC] 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));
}
