#!/usr/bin/python3
# -*- coding: utf-8 -*-

#  Copyright © 2012-2013  B. Clausius <barcc@gmx.de>
#
#  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 3 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, see <http://www.gnu.org/licenses/>.



import sys, os
sys.path.insert(0, '.')
import pickle as pickle
from multiprocessing import Pool, cpu_count

from pybiklib import model
model.use_modeldata = False


dedup_data = []
cnt_dups = []
cnt_values = []

def dedup_reset():
    dedup_data[:] = [[] for unused_i in range(6)]
    cnt_dups[:] = [0] * 6
    cnt_values[:] = [0] * 6
    
def pool_dedup(level, value):
    if isinstance(value, dict):
        iterator = list(value.items())
    elif isinstance(value, list):
        iterator = enumerate(value)
    elif isinstance(value, tuple):
        iterator = []
    else:
        return value
    cnt_values[level] += 1
    try:
        didx = dedup_data[level].index((type(value), value))
    except ValueError:
        dedup_data[level].append((type(value), value))
        for k, v in iterator:
            v = pool_dedup(level+1, v)
            value[k] = v
    else:
        dtype, dvalue = dedup_data[level][didx]
        assert value == dvalue and type(value) == type(dvalue) == dtype, (value, dvalue)
        cnt_dups[level] += 1
        value = dvalue
    return value
        
def pool_functions(parallel):
    pool = None
    try:
        if parallel > 1:
            pool = Pool(processes=parallel)
    except OSError as e:
        print('process pool not available ({}):'.format(e))
        print('  maybe /dev/shm is not mounted,')
        print('  deactivating multiprocessing')
    sys.stdout.flush() # when Pool(…) fails this line is 
    if pool is not None:
        return pool.imap_unordered
    else:
        return map
        
def enum_modelfiles(dirname, testfunc):
    ignored = []
    modelfiles = []
    for Model in model.models:
        for i in range(1, 11):
            for j in range(1, 11):
                for k in range(1, 11):
                    sizes = Model.norm_sizes((i, j, k))
                    filename = Model.get_datafilename(sizes)
                    filename = os.path.join(dirname, filename)
                    if filename in modelfiles or filename in ignored:
                        continue
                    if testfunc(filename):
                        modelfiles.append(filename)
                    else:
                        ignored.append(filename)
    if modelfiles:
        for filename in sorted(ignored):
            print('skipping', filename)
    return sorted(modelfiles, reverse=True)
    
def pool_enum_modelfile(filename):
    sizes_list = []
    for Model in model.models:
        for i in range(1, 11):
            for j in range(1, 11):
                for k in range(1, 11):
                    sizes = Model.norm_sizes((i, j, k))
                    if os.path.basename(filename) != Model.get_datafilename(sizes):
                        continue
                    if (Model, sizes) in sizes_list:
                        continue
                    sizes_list.append((Model, sizes))
    return sizes_list
    
def pool_create_modelfiledata(sizes_list):
    savedata = {}
    for Model, sizes in sizes_list:
        modelobj = Model(sizes, None)
        savedata[(Model.type, sizes)] = modelobj.get_savedata()
    return savedata
    
def pool_create_modelfile(args):
    filename, fast = args
    sizes_list = pool_enum_modelfile(filename)
    savedata = pool_create_modelfiledata(sizes_list)
    if not fast:
        dedup_reset()
        savedata = pool_dedup(0, savedata)
    with open(filename, 'wb') as datafile:
        pickle.dump(savedata, datafile, -1)
    vals = ['%6s' % v for v in cnt_values]
    dups = ['%6s' % d for d in cnt_dups]
    return 'generated {} with {} models\n  vals: {}\n  dups: {}'.format(
                    filename, len(sizes_list), ' '.join(vals), ' '.join(dups))
    
def create_modeldata(dirname, testfunc=None, parallel=1, fast=False):
    if testfunc is None:
        testfunc = lambda arg: True
    imap_model = pool_functions(parallel)
    modelfiles = enum_modelfiles(dirname, testfunc)
    for lines in imap_model(pool_create_modelfile, [(m, fast) for m in modelfiles]):
        print(lines)
        sys.stdout.flush()
    
    
if __name__ == '__main__':
    create_modeldata('data/models', parallel=cpu_count())
    #create_modeldata('data/models', parallel=1)
    

