import os
import errno
import shutil

from cse.util import PackerUtil
from cse.indexing.commons import PARTIAL_THRESHOLD, SKIP_SIZE


class DocumentMapBuilder:

    def __init__(self, document_map_index, document_map_dict):
        if not os.path.exists(os.path.dirname(document_map_index)):
            try:
                os.makedirs(os.path.dirname(document_map_index))
            except OSError as exc: # Guard against race condition
                if exc.errno != errno.EEXIST:
                    raise

        self.__document_map_index = document_map_index
        self.__document_map_dict = document_map_dict
        self.__index = {}
        if os.path.exists(self.__document_map_index):
            os.remove(self.__document_map_index)
        if os.path.exists(self.__document_map_dict):
            os.remove(self.__document_map_dict)
        dir = os.path.dirname(self.__document_map_index)
        if os.path.isdir(os.path.join(dir, 'document_map_partials')):
            shutil.rmtree(os.path.join(dir, 'document_map_partials'))

    def close(self):
        self._build_partial_files()
        doc_dict = self._build_index_and_dict()
        PackerUtil.packToFile(doc_dict, self.__document_map_dict, type=PackerUtil.PICKLE)

    def _build_partial_files(self):
        i = 0
        content = 0
        partial_list = []
        for cid in self.__index:
            partial_list.append((cid, self.__index[cid]))
            content += 1
            if content >= PARTIAL_THRESHOLD:
                partial_list.sort()
                self.__save_partial_index(partial_list, i)
                partial_list = []
                content = 0
                i += 1
        partial_list.sort()
        self.__save_partial_index(partial_list, i)

    def insert(self, cid, pointer):
        self.__index[cid] = pointer

    def get(self, cid):
        return self.__index[cid]

    def listCids(self):
        return [cid for cid in self.__index]

    def __enter__(self):
        return self.open()

    def __exit__(self, type, value, traceback):
        self.close()

    def __save_partial_index(self, partial_list, i):
        dir = os.path.dirname(self.__document_map_index)
        file_name = os.path.basename(self.__document_map_index)
        if not os.path.exists(os.path.join(dir, 'document_map_partials')):
            os.makedirs(os.path.join(dir, 'document_map_partials'))

        with open(os.path.join(dir, 'document_map_partials', file_name + str(i)), 'w') as partial:
            for tuple in partial_list:
                partial.write(str(tuple[0]) + ',' + str(tuple[1]) + '\n')

    def _build_index_and_dict(self):
        doc_dict = ([], [])
        dir = os.path.dirname(self.__document_map_index)
        partials_dir = os.path.join(dir, 'document_map_partials')
        partial_files = []
        current_lines = []
        skips = SKIP_SIZE
        with open(self.__document_map_index, 'w') as out_index:
            for file in os.listdir(partials_dir):
                partial_files.append(open(os.path.join(partials_dir, file)))

            for file_handle in partial_files:
                current_lines.append(self._read_partial_line(file_handle))

            while partial_files:
                min_index = current_lines.index(min(current_lines))
                skips += 1
                if skips >= SKIP_SIZE:
                    doc_dict[0].append(int(current_lines[min_index][0]))
                    doc_dict[1].append(out_index.tell())
                    skips = 0
                out_index.write(','.join(current_lines[min_index]))
                current_lines[min_index] = self._read_partial_line(partial_files[min_index])
                if not current_lines[min_index]:
                    partial_files[min_index].close()
                    del partial_files[min_index]
                    del current_lines[min_index]
        return doc_dict

    def _read_partial_line(self, file_handle):
        line = file_handle.readline()
        if line:
            return tuple(line.split(','))
        else:
            return None

@@ -641,10 +641,58 @@
 x = 
-%7B%7D
+%5B%5D%0A        self.__i = 0%0A        self.__content = 0
 %0A   
@@ -1125,36 +1125,86 @@
   self._
-build_partial_files(
+_index.sort()%0A        self.__save_partial_index(self.__index, self.__i
 )%0A      
@@ -1348,184 +1348,77 @@
 def 
-_build_partial_files(self):%0A        i = 0%0A        content = 0%0A        partial_list = %5B%5D%0A        for cid in self.__index:%0A            partial_list.append((cid, self.__index%5Bcid%5D
+insert(self, cid, pointer):%0A        self.__index.append((cid, pointer
 ))%0A 
@@ -1416,36 +1416,39 @@
 inter))%0A        
-    
+self.__
 content += 1%0A   
@@ -1452,23 +1452,26 @@
         
- 
-   
 if 
+self.__
 content 
@@ -1508,32 +1508,28 @@
     
-    partial_list
+self.__index
 .sort()%0A
     
@@ -1520,28 +1520,24 @@
 ndex.sort()%0A
-    
             
@@ -1566,53 +1566,56 @@
 dex(
-partial_list, i)%0A                partial_list
+self.__index, self.__i)%0A            self.__index
  = %5B
@@ -1632,198 +1632,52 @@
     
-    
-content = 0%0A                i += 1%0A        partial_list.sort()%0A        self.__save_partial_index(partial_list, i)%0A%0A    def insert(self, cid, pointer):%0A        self.__index%5Bcid%5D = pointer
+self.__content = 0%0A            self.__i += 1
 %0A%0A  
@@ -3718,32 +3718,68 @@
 ines%5Bmin_index%5D%0A
+        # TODO delete partial files%0A
         return d

build direct doc map partials