import {
  AfterViewInit,
  Component,
  ElementRef,
  EventEmitter,
  Input,
  OnDestroy,
  Output,
  Renderer2,
  ViewChild,
} from '@angular/core';
import { FlatEmoji, fromHexcodeToCodepoint } from 'emojibase';
import * as emojiBaseData from 'emojibase-data/en/data.json';
import { AppStorage } from 'src/app/shared/app-storage';

declare var twemoji;

@Component({
  selector: 'app-emoji-picker',
  templateUrl: './emoji-picker.component.html',
  styleUrls: ['./emoji-picker.component.scss'],
})
export class EmojiPickerComponent implements OnDestroy, AfterViewInit {
  private RECENT_EMOJIES_MAX_CAP = 15;

  public categories = [
    { name: 'Recently used', emojies: [], icon: 'schedule' },
    { name: 'Emotions', emojies: [], icon: 'emoji_emotions' },
    { name: 'People', emojies: [], icon: 'emoji_people' },
    { name: 'Skin & Hair', emojies: [], icon: 'color_lens' },
    { name: 'Pets', emojies: [], icon: 'emoji_nature' },
    { name: 'Foods & Drinks', emojies: [], icon: 'emoji_food_beverage' },
    { name: 'Transportation', emojies: [], icon: 'emoji_transportation' },
    { name: 'Events', emojies: [], icon: 'emoji_events' },
    { name: 'Objects', emojies: [], icon: 'emoji_objects' },
    { name: 'Symbols', emojies: [], icon: 'emoji_symbols' },
    { name: 'Flags', emojies: [], icon: 'emoji_flags' },
  ];

  public shortcodes;
  public someData;

  // for lazyload, it is a gray rectangle
  public assetFolder = 'assets/img/twemoji/72x72/';
  public assetExt = '.png';
  public defaultImage = this.assetFolder + '1f532' + this.assetExt;

  /**
   * The selected current group, for the header, ex: emoji_nature
   */
  public activeGroup = '';

  /**
   * Emoji from emojibase
   */
  public allEmoji;

  @Output() readonly selectEmojiEvent = new EventEmitter<string>();
  @ViewChild('emojiCollection') emojiCollectionNode: ElementRef;
  onScrollEvent;

  constructor(private renderer: Renderer2) {
    this.allEmoji = [];
    // load from cdn
    /*fetchFromCDN("en/data.json", opt).then((res) => {
      this.allEmoji = res;
    });*/
    this.allEmoji = emojiBaseData; // load locally (csp)
  }

  /**
   * scroll into the group
   * @param group group icon name: emoji_nature
   */
  jumpToGroup(group) {
    let element = this.emojiCollectionNode.nativeElement.querySelector(
      '[data-group="' + group + '"]'
    );
    if (element) {
      let y = element.offsetTop;
      this.emojiCollectionNode.nativeElement.scrollTo(0, y, { behavior: 'smooth' });
    }
  }

  ngAfterViewInit(): void {
    // these are only the hexcode identifiers of <FlatEmoji>emojies
    const localStorageEmojies = <string[]>(
      JSON.parse(AppStorage.getItem('recentlyUsedEmojies') || '[]')
    );
    let recentlyUsedEmojies: { hexcode: string; index: number }[] = [];
    localStorageEmojies.forEach((hexcode, index) => {
      recentlyUsedEmojies.push({ hexcode, index });
    });
    this.categories[0].emojies = [];

    // init all the emoji
    for (let i = 0; i < this.allEmoji.length; i++) {
      // finds and replaces hexcodes with corresponding emojies in recentlyUsedEmojies
      if (recentlyUsedEmojies.length > 0) {
        let recentIndex = recentlyUsedEmojies.findIndex(
          (e) => e.hexcode === this.allEmoji[i].hexcode
        );
        if (recentIndex >= 0) {
          twemoji.parse(this.allEmoji[i].emoji, {
            callback: (icon, options) => {
              if (icon) {
                let emoji = this.allEmoji[i];
                emoji.twitter = icon;
                this.categories[0].emojies[recentlyUsedEmojies[recentIndex].index] = emoji;
              }
            },
          });
          recentlyUsedEmojies.splice(recentIndex, 1);
        }
      }
      // init emoji
      twemoji.parse(this.allEmoji[i].emoji, {
        callback: (icon, options) => {
          if (icon) {
            let emoji = this.allEmoji[i];
            emoji.twitter = icon;

            if (i < 26) emoji.group = 8; // put the A-Z letters into the symbols
            if (!emoji.group) emoji.group = 0; // zero groups emojies doesn't have group attr

            const groupNumber = emoji.group + 1; // 'recently used' custom category shifts the array with 1

            if (!this.categories[groupNumber])
              this.categories[groupNumber] = {
                name: 'Untitled',
                emojies: [],
                icon: 'insert_photo',
              }; // expand the categories
            this.categories[groupNumber].emojies.push(emoji);
          }
        },
      });
    }

    // detect on which group you are
    this.onScrollEvent = (event) => {
      let y = this.emojiCollectionNode.nativeElement.scrollTop;
      let childs = this.emojiCollectionNode.nativeElement.childNodes;
      for (let i = 0; i < childs.length; i++) {
        if (childs[i].offsetTop - 5 <= y && childs[i].offsetTop + childs[i].offsetHeight > y + 5) {
          this.activeGroup = childs[i].dataset.group;
          return;
        }
      }
    };

    /**
     * <div *ngFor="let group of categories" class="group" [attr.data-group]="group.icon">
            <div class="group-name"><a id="{{group.icon}}">{{group.name}}</a></div>
            <div class="emoji-list">
                <span *ngFor="let emoji of group.emojies" (click)="selectEmoji(emoji)">
                    <img *ngIf="emoji.twitter" [defaultImage]="defaultImage" [lazyLoad]="'https://twemoji.maxcdn.com/v/13.0.1/72x72/'+emoji.twitter+'.png'"> 
                </span>
            </div>
        </div>
     */
    // ngFor is to slow to generate DOM for 2k emojies
    this.categories.forEach((group) => {
      let groupNode = this.renderer.createElement('div');
      this.renderer.addClass(groupNode, 'group');
      this.renderer.setAttribute(groupNode, 'data-group', group.icon);

      let groupTitleNode = this.renderer.createElement('div');
      this.renderer.addClass(groupTitleNode, 'group-name');

      let groupLink = this.renderer.createElement('a');
      this.renderer.setProperty(groupLink, 'id', group.icon);
      this.renderer.setProperty(groupLink, 'innerHTML', group.name);

      let emojiList = this.renderer.createElement('div');
      this.renderer.addClass(emojiList, 'emoji-list');

      this.renderer.appendChild(groupTitleNode, groupLink);
      this.renderer.appendChild(groupNode, groupTitleNode);
      this.renderer.appendChild(groupNode, emojiList);

      group.emojies.forEach((emoji) => {
        if (emoji.hasOwnProperty('twitter')) {
          let emojiNode = this.renderer.createElement('span');
          emojiNode.addEventListener('click', () => {
            this.selectEmoji(emoji);
          });
          let emojiImage = this.renderer.createElement('img');
          this.renderer.setProperty(emojiImage, 'loading', 'lazy');
          this.renderer.setProperty(
            emojiImage,
            'src',
            this.assetFolder + emoji.twitter + this.assetExt
          );

          this.renderer.appendChild(emojiNode, emojiImage);
          this.renderer.appendChild(emojiList, emojiNode);
        }
      });

      this.renderer.appendChild(this.emojiCollectionNode.nativeElement, groupNode);
    });

    this.emojiCollectionNode.nativeElement.addEventListener('scroll', this.onScrollEvent);
  }

  ngOnDestroy(): void {
    this.emojiCollectionNode.nativeElement.removeEventListener('scroll', this.onScrollEvent);
  }

  /**
   * Emoji select handler in picker
   * @param emoji selected emoji
   */
  public selectEmoji(emoji: FlatEmoji) {
    this.selectEmojiEvent.emit(emoji.emoji);
    this.categories[0].emojies = this.pushEmojiToLocalStorage(emoji);
    this.refreshRecentEmojiesDOM(emoji);
  }

  /**
   * Push emoji to Recently used emojies box - moves it to the first place when already in box
   * @param emoji selected emoji
   */
  private refreshRecentEmojiesDOM(emoji: FlatEmoji & { twitter?: string }) {
    if (emoji.hasOwnProperty('twitter')) {
      const recentList = this.emojiCollectionNode.nativeElement.childNodes[0].childNodes[1];
      // locally calculated  childElementCount, because this.renderer.removeChild/appendChild/insertBefore is async
      let localChildElementCount = recentList.childElementCount;

      let emojiNode = null;
      for (let i = 0; i < localChildElementCount; i++) {
        const recentNode = recentList.childNodes[i];
        if (recentNode.firstChild.src.includes(emoji.twitter.toLowerCase())) {
          emojiNode = recentList.children[i]; // found emoji in recently used list
          --localChildElementCount;
          break;
        }
      }

      if (!emojiNode) {
        // creating emojiNode with emojiImage
        // <span (click)="selectEmoji(emoji)">
        //   <img *ngIf="emoji.twitter" [defaultImage]="defaultImage" [lazyLoad]="'https://twemoji.maxcdn.com/v/13.0.1/72x72/'+emoji.twitter+'.png'">
        // </span>
        emojiNode = this.renderer.createElement('span');
        emojiNode.addEventListener('click', () => {
          this.selectEmoji(emoji);
        });
        const emojiImage = this.renderer.createElement('img');
        this.renderer.setProperty(emojiImage, 'loading', 'lazy');
        this.renderer.setProperty(
          emojiImage,
          'src',
          this.assetFolder + emoji.twitter + this.assetExt
        );
        this.renderer.appendChild(emojiNode, emojiImage);
      }

      // unshift emoji to the first place in array, or insert as first element
      if (localChildElementCount) {
        recentList.insertBefore(emojiNode, recentList.children[0]);
        ++localChildElementCount;

        // recent emojies per list is capped at max
        if (localChildElementCount > this.RECENT_EMOJIES_MAX_CAP) {
          const removables = [];
          for (let i = this.RECENT_EMOJIES_MAX_CAP; i < localChildElementCount; i++) {
            removables.push(recentList.children[i]);
          }
          for (const removable of removables) {
            console.log('should remove? ', removable);
            this.renderer.removeChild(recentList, removable);
            --localChildElementCount;
          }
        }
      } else {
        this.renderer.appendChild(recentList, emojiNode);
        ++localChildElementCount;
      }
    }
  }

  /**
   * Pushes emoji to localStorage
   * @param emoji selected emoji
   * @returns {string[]} updated recentlyUsedEmojies state in localStorage
   */
  private pushEmojiToLocalStorage(emoji: FlatEmoji) {
    const recentlyUsedEmojies = <string[]>(
      JSON.parse(AppStorage.getItem('recentlyUsedEmojies') || '[]')
    );

    const filtered = recentlyUsedEmojies.filter((hexcode) => hexcode !== emoji.hexcode);
    filtered.unshift(emoji.hexcode);
    // recent emojies per list is capped at max
    AppStorage.setItem(
      'recentlyUsedEmojies',
      JSON.stringify(filtered.slice(0, this.RECENT_EMOJIES_MAX_CAP))
    );
    return filtered;
  }
}
