/*****************************************************************************
Open Source MIT License

Copyright (c) 2017 Matthew Ahmed

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*****************************************************************************/

#pragma once

#define SONG_MAX_REPEATS 255
//#define SONG_ARRAY_SIZE 131072 // 1 MB overhead in shuffle. Change to ini setting?
#define INITIAL_ARRAY_SIZE 32768  // Expands if necessary

#include <hash_map>

class ZWin;
//class hash_map;
//struct hash_compare_lpctstr;

// hash_compare replacement for LPTSTR hash key
// Derived from char* example by hash_map author P.J. Plauger:
// http://www.tech-archive.net/Archive/VC/microsoft.public.vc.stl/2004-06/0069.html
struct hash_compare_lpctstr
{ // define hash function for strings
  enum
  { // parameters for hash table
    bucket_size = 8, // 0 < bucket_size  // default is 4
    min_buckets = 256 // min_buckets = 2 ^^ N, 0 < N // default is 8
  };

/*
From author's comment in page linked above:
bucket_size * min_buckets is the maximum capacity of the initial table.
If it's close to the final size of the table, you'll probably grow the
table much faster than if you just grow it incrementally. If you make
the table too large, however, the cost of adding an element can also go
up (because of the need to update many hash table elements on each
insertion when the density is very low). Make this product close to
the middle of the range of table sizes. 
*/

  size_t operator()(LPCTSTR s1) const
  { // hash string s1 to size_t value
    //const unsigned char *p = (const unsigned char *) s1;

    size_t hashval = 0; // will add values based on string content
    // hashval seems to be some arbitrary value used decide which bucket.
    // Apparently more even distribution of values increases efficiency.
    // For wide char, and strings having same start and end, replace
    // two lines below below from author's example

//    while (*p != '\0')
//    hashval += *p++; // or whatever

    // Beginning of path is same, and end frequently repeated .mp3, .wma
    // Get more variation with 10 chars going back from last char before extension.
    size_t nLen = _tcslen(s1);
    if (nLen < 6) return hashval; // Should never happen, but don't die if it does.
    const _TUCHAR * pT = (const _TUCHAR *) &s1[nLen - 5];
    const _TUCHAR * pMinT = nLen > 14 ? pT - 10 : (const _TUCHAR *) s1;

    // Add up values of individual bytes in str segement, i.e. char's
    // With TCHAR is wide char, most will have first byte 0, no effect.
    unsigned char * pC = (unsigned char *) pT;    // Start at last char before ".mp3",
    unsigned char * pMinC = (unsigned char *) pMinT; // then iterate backwards to this

    while (pC > pMinC)
      hashval += *pC--;

    return (hashval);
  } // struct hash_compare_lpctstr

  bool operator()(LPCTSTR s1, LPCTSTR s2) const
  { // test if s1 ordered before s2
    return (_tcscmp(s1, s2) < 0);
  }
};

class CSongArray
{
public:
  CSongArray(ZWin * pOwner = NULL);
  ~CSongArray(void);

  INT AddSong(LPCTSTR sListLine, INT nOccurrences = 1);
  INT IncrementOrAddSong(LPCTSTR sListLine, INT nOccurrences = 1);
  // Add in v.3. Formerly inlined in IncrementOrAddSong, but now sometimes
  // directly call IncrementSong when already know song is in array
  INT IncrementSong(INT nSongIndex, LPCTSTR sPath, INT nOccurrences = 1);

  LPCTSTR GetSong(INT nArrayIndex);
  INT GetCountOneSong(INT nArrayIndex);
  BOOL SetCountOneSong(INT nArrayIndex, INT nOccurrences);
  INT GetSongIndex(LPCTSTR sListLine);

  INT GetSize(void);
  INT GetListLength(void);
  INT GetMaxOccurrences(void);

  BOOL SongsTheSame(INT nSong1, INT nSong2);

	BOOL SetError(LPCTSTR sErr1, LPCTSTR sErr2 = NULL);
	//BOOL AddToError(LPCTSTR sErr);
	LPCTSTR GetError(BOOL bClear = TRUE);
	BOOL ShowError(LPCTSTR sCapt, LPCTSTR sPre = NULL, LPCTSTR sPost = NULL);
	void ClearError();
  void SetOwner(ZWin * pOwner);
  BOOL CopyToAnother(CSongArray * psaTarget);

protected:

  stdext::hash_map<LPCTSTR, INT, hash_compare_lpctstr> m_path_arrix_hash;
  // Does declaring iterator once save any processing, instead of having local
  // variable in lookup function called thousands of times?
  stdext::hash_map<LPCTSTR, INT, hash_compare_lpctstr>::const_iterator m_path_arrix_iter;

  BOOL DoubleArraySize();

  typedef struct SongAndCount
  {
	  LPTSTR sLine;   // Can be duplicated with nCount = 1, or can iterate nCount
	  INT nCount;
  } SongStruct;

//  // Can't shuffle more than this many. RAND_MAX is 0x7FFF (32K)
//  SongStruct m_sspSongs[RAND_MAX + 1];  // Array of SongStruct
  // GetRandomUint now uses two calls to rand(), max larger than ever use here
  //SongStruct m_sspSongs[SONG_ARRAY_SIZE];  // Array of SongStruct
  SongStruct * m_sspSongs;  // Array of SongStruct, allocate in constructor

  INT m_nMaxSongCount;
  INT m_nListLength;  // != m_uNextUnusedInd if any songs with nCount > 1

  INT m_nMaxInd;
  INT m_nNextUnusedInd; //  equals count of allocated SongStructs 
  void ClearArray(void);

  HWND m_hDlg;
  ZWin * m_pOwner;
  TCHAR m_sError[8192];
  TCHAR m_sReportError[8192];

  TCHAR sBS[2048];


};
