package h2o.utils.key;


import com.jenkov.db.itf.IDaos;
import com.jenkov.db.itf.IResultSetProcessor;
import com.jenkov.db.itf.PersistenceException;
import h2o.common.Tools;
import h2o.common.concurrent.LockMap;
import h2o.common.util.collections.tuple.Tuple2;
import h2o.common.util.collections.tuple.Tuple3;
import h2o.common.util.collections.tuple.TupleUtil;
import h2o.common.util.dao.butterflydb.ButterflyDao;
import h2o.common.util.dao.butterflydb.ButterflyDb;
import h2o.common.util.dao.butterflydb.ButterflyDbCallback;
import h2o.common.util.math.IntArith;
import h2o.dao.Dao;
import h2o.dao.DbUtil;
import h2o.dao.TxCallback;
import org.apache.commons.lang.StringUtils;

import java.math.BigInteger;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;

public class KeyGen {

    public static final String DEFAULT_CYCLICSPACE = "_null_";

    static final int RETRYTIMES = 10;

    private static final LockMap lockMap = new LockMap();


    private static final String SELSEQ = DbUtil.sqlTable.getSql("selseq");
    private static final String INSSEQ = DbUtil.sqlTable.getSql("insseq");
    private static final String UPDSEQ = DbUtil.sqlTable.getSql("updseq");



    static String leftPad(String sn, int length) {
        return StringUtils.leftPad(sn, length, '0');
    }


    public static String getKey(String seq, int length) {
        return leftPad(getKey(seq), length);
    }

    public static String getKey(String seqObj, String cyclicSpace, int length) {
        return leftPad(getKey( seqObj, cyclicSpace ), length);
    }


    public static String getKey(String seqObj, CyclicSpaceSetter cyclicSpaceSetter , int length) {
        return leftPad(getKey( seqObj, cyclicSpaceSetter ), length);
    }


    public static String getKey(String seq) {
        return getKey(seq, (String)null);
    }


    public static String getKey(String seqObj, String cyclicSpace) {
        cyclicSpace = StringUtils.isBlank(cyclicSpace) ? DEFAULT_CYCLICSPACE : cyclicSpace;
        return getKey( seqObj , new DefaultCyclicSpaceSetter(cyclicSpace) );
    }


    public static String getKey( String seqObj , CyclicSpaceSetter cyclicSpaceSetter ) {

        Lock lock = lockMap.getLock(seqObj);
        lock.lock();
        try {

            String k = null;
            int n = 0;
            while (((k = incAndUpdateKey(seqObj, cyclicSpaceSetter)) == null) && (n++ < RETRYTIMES)) {
                try {
                    TimeUnit.MILLISECONDS.sleep(20L);
                } catch (InterruptedException localInterruptedException) {
                }
            }

            if (k == null) {
                throw new RuntimeException("getKey 失败!!!");
            }

            Tools.log.info("getKey({})============>{}", seqObj, k);

            return k;

        } finally {
            lock.unlock();
        }
    }



    public static BigInteger getNumberKey(String seq) {
        return new BigInteger(getKey(seq));
    }

    public static BigInteger getNumberKey(String seqObj, String cyclicSpace) {
        return new BigInteger(getKey(seqObj, cyclicSpace));
    }

    public static BigInteger getNumberKey(String seqObj, CyclicSpaceSetter cyclicSpaceSetter) {
        return new BigInteger(getKey(seqObj, cyclicSpaceSetter));
    }


    private static String incAndUpdateKey(final String key, final CyclicSpaceSetter cyclicSpaceSetter) {
        Tuple2<String, String> kk = incAndUpdateKey(key, cyclicSpaceSetter, "1");
        return kk == null ? null : kk.e0;
    }


    /**
     * @return 新值,原值
     */
    static Tuple2<String, String> incAndUpdateKey(final String key, final CyclicSpaceSetter cyclicSpaceSetter , final String incNum) {

        final String[] rr = {"-1", "-1"};

        DbUtil.getDb("common" ).tx( new TxCallback<Void>() {

            @Override
            public Void doCallBack(Dao dao) throws Exception {

                Map<String,Object> m =  dao.get( SELSEQ, "seqobj", key );

                if ( m == null ) {

                    try {

                        dao.update( INSSEQ , "seqobj", key , "cyclicspace" , DEFAULT_CYCLICSPACE );

                    } catch (Exception e) {
                        Tools.log.error(e);
                    }

                    rr[0] = null;

                } else {

                    try {

                        String oldCyc = (String)m.get("cyclicspace");
                        String oldNo  = (String)m.get("seqno");

                        String cyclicSpace = cyclicSpaceSetter.getNewCyclicSpace( oldCyc , oldNo );
                        if ( cyclicSpace == null ) {
                            cyclicSpace = DEFAULT_CYCLICSPACE;
                        }

                        String r0 = ( cyclicSpace.equals( oldCyc) ) ?  oldNo : "0";

                        String r = IntArith.add( r0 , incNum );

                        dao.update( UPDSEQ , "seqobj", key  , "cyclicspace" , cyclicSpace , "seqno" , r );

                        rr[0] = r0;
                        rr[1] = r;

                    } catch ( Exception e ) {
                        Tools.log.error(e);
                        rr[0] = null;
                    }

                }

                return null;
            }

        });


        return rr[0] == null ? null : TupleUtil.t( rr[0], rr[1] );

    }





}
@@ -5098,17 +5098,16 @@
 r%5B0%5D = r
-0
 ;%0A      
@@ -5133,16 +5133,17 @@
 r%5B1%5D = r
+0
 ;%0A%0A     

重构 KeyGen