import { LunarDateInfo } from '../types';
import { LUNAR_VALIDITY_RANGE } from './utils';

const { MIN_YAER: minYear, MIN_MONTH: minMonth, MIN_DAY: minDay, MAX_YAER: maxYear } = LUNAR_VALIDITY_RANGE;

// 闰月数据压缩：1位闰月大小+12位平月大小及4位长度闰月月份转2进制，再转32进制
const monthData = [
  'iuo', 'in0', '19bg', 'l6l', '1kj0', '1mag', '2pak', 'll0', '16mg', 'lei',
  'in0', '19dm', '196g', '1kig', '3kil', '1da0', '1ll0', '1bd2', '15dg', '2ibn',
  'ibg', '195g', '1d5l', 'qig', 'ra0', '3aqk', 'ar0', '15bg', 'kni', 'ibg',
  'pb6', '1l50', '1qig', 'rkl', 'mmg', 'ar0', '31n3', '14n0', '3i6n', '1iag',
  '1l50', '3m56', '1dag', 'll0', '39dk', '9eg', '14mg', '1kli', '1aag', '1dan',
  'r50', '1dag', '2kql', 'jd0', '19dg', '2hbj', 'klg', '1ad8', '1qag', 'ql0',
  '1bl6', '1aqg', 'ir0', '1an4', '19bg', 'kj0', '1sj3', '1mag', 'mqn', 'll0',
  '15mg', 'jel', 'img', '196g', '1l6k', '1kig', '1lao', '1da0', '1dl0', '35d6',
  '15dg', 'idg', '1abk', '195g', '1cjq', 'qig', 'ra0', '1bq6', '1ar0', '15bg',
  'inl', 'ibg', 'p5g', 't53', '1qig', 'qqo', 'le0', '1ar0', '15ml', '14n0',
  '1ib0', '1mak', '1l50', '1mig', 'tai', 'll0', '1atn', '9eg', '14mg', '1ill',
  '1aag', '1d50', '1el4', '1bag', 'lep', 'it0', '19dg', '2kbm', 'klg', '1a9g',
  'uak', 'ql0', '1bag', 'mqi', 'ir0', '19n6', '1970', '1kj0', '1qj5', '1l9g',
  'ml0', 'tl3', '15mg', 'inr', 'img', '196g', '3k5m', '1kig', '1l90', '1na5',
  '1dd0', 'lmg', 'ldi', 'idg', '19bn', '195g', '1aig', '3cil', 'r90', '1bd0',
  '2ir3', '14rg', 'ifo', 'ibg', 'p5g', '2q56', '1qig', 'qp0', '39m4', '1an0',
  '18n0', '1kn3', '1ib0', '1lan', '1l50', '1mig', 'nal', 'll0', '19mg', 'lek',
  'kmg', '1ado', '1aag', '1d50', '1dl6', '1bag', 'ld0', '1at4', '19dg', 'klg',
  '1cjj', 'q9g', 'spn', 'ql0', '1bag', '2iql', 'ir0', '19bg', 'l74', '1kb0',
  '1qb8', '1l90', '1ml0', '2ql6', 'lmg', 'in0', '1aek', '18mg', '1kag', '1sii',
  '1l90'
];
// 月份
const monthMap = ['正', '二', '三', '四', '五', '六', '七', '八', '九', '十', '冬', '腊'];
// 十位
const dayMap = ['初一', '初二', '初三', '初四', '初五', '初六', '初七', '初八', '初九', '初十', '十一', '十二', '十三', '十四', '十五', '十六', '十七', '十八', '十九', '二十', '廿一', '廿二', '廿三', '廿四', '廿五', '廿六', '廿七', '廿八', '廿九', '三十'];
// 参考时间点
const startTime = Date.UTC(minYear, minMonth - 1, minDay, 0, 0, 0);

// 获取农历年闰月
export function getLeapMonth(lYear: number) {
  let data = parseInt(monthData[lYear - minYear], 32);
  return data & 0xf;
}

// 获取农历年长度
export function getLunarYearDays(lYear: number) {
  let offset = 0;
  let data = parseInt(monthData[lYear - minYear], 32);
  for (let i = 1 << 15; i >= 1 << 4; i >>= 1) {
    offset += (data & i) ? 30 : 29;
  }
  if (getLeapMonth(lYear)) {
    offset += (data & 1 << 16) ? 30 : 29;
  }
  return offset;
}

// 获得农历月份天数
export function getLunarMonthDays(lYear: number, lMonth: number, isLeap?: boolean) {
  let leapMonth = getLeapMonth(lYear);
  let data = parseInt(monthData[lYear - minYear], 32);
  let days = data & 1 << (16 - lMonth) ? 30 : 29;
  if (isLeap && lMonth == leapMonth) {
    days = data & 1 << 16 ? 30 : 29;
  }
  return days;
}

// 农历日期转时间戳
export function getTimestampByLunar(lYear: number, lMonth: number, lDay: number, isLeap: boolean) {
  let data = parseInt(monthData[lYear - minYear], 32);
  // 有效性验证
  if (lYear < minYear || lYear > maxYear) {
    return null;
  }
  if (lMonth < 1 || lMonth > 12) {
    return null;
  }
  let leapMonth = getLeapMonth(lYear);
  if (isLeap && leapMonth != lMonth) {
    return null;
  }
  let days = (isLeap ? data & 1 << 16 : 1 << (17 - lMonth)) ? 30 : 29;
  if (lDay > days) {
    return null;
  }
  // 时间戳获取
  let offset = 0;

  for (let year = minYear; year < lYear; year++) {
    offset += getLunarYearDays(year);
  }
  for (let month = 1; month < lMonth || isLeap && month == lMonth && lMonth == leapMonth; month++) {
    offset += data & 1 << (16 - month) ? 30 : 29;
  }
  if (leapMonth && lMonth > leapMonth) {
    offset += data & 1 << 16 ? 30 : 29;
  }
  offset += lDay;
  return startTime + offset * 86400000;
}

/**
 * 时间戳转农历日期
 * @param timestamp 时间戳
 * @returns 计算得到的农历日期
 */
export function getLunarByTimestamp(timestamp: number): LunarDateInfo | null {
  let offset = Math.floor((timestamp - startTime) / 86400000);
  let lYear = 0, lMonth = 0, lDay = 0, isLeap = false;
  let days;
  if (offset <= 0) {
    return null;
  }
  let count = 0;
  for (lYear = minYear; lYear <= maxYear; lYear++) {
    days = getLunarYearDays(lYear);
    if (count + days >= offset) {
      break;
    }
    count += days;
  }
  let data = parseInt(monthData[lYear - minYear], 32);
  let leapMonth = getLeapMonth(lYear);
  offset -= count;
  count = 0;
  for (lMonth = 1; lMonth <= 12; lMonth++) {
    days = data & 1 << (16 - lMonth) ? 30 : 29;
    if (count + days >= offset) {
      break;
    }
    count += days;
    if (leapMonth && lMonth == leapMonth) {
      days = data & 1 << 16 ? 30 : 29;
      if (count + days >= offset) {
        isLeap = true;
        break;
      }
      count += days;
    }
  }
  lDay = offset - count;
  return {
    lYear: lYear,
    lMonth: lMonth,
    lDay: lDay,
    isLeap: isLeap,
    lMonthZH: (isLeap ? '闰' : '') + monthMap[lMonth - 1] + '月',
    lDayZH: dayMap[lDay - 1]
  };
}