import {
  Type,
} from '../type';

import {
  TypeUtil,
} from '../util/TypeUtil';

import { AnnotatedElement } from './AnnotatedElement';
import { Annotation } from './Annotation';

export abstract class AccessibleObject implements AnnotatedElement {
  private _annotations: Map<Type<any>, Annotation>;

  protected constructor() {
    this._annotations = new Map();
  }

  public _addAnnotation<AnnotationType extends Annotation>(annotation: AnnotationType): void {
    this._annotations.set(TypeUtil.getType(annotation), annotation);
  }

  public isAnnotationPresent<AnnotationType extends Annotation>(annotationClass: Type<AnnotationType>): boolean {
    return null !== this.getAnnotation(annotationClass);
  }

  public getOwnAnnotation<AnnotationType extends Annotation>(annotationClass: Type<AnnotationType>): AnnotationType | undefined {
    return <AnnotationType>this._annotations.get(annotationClass);
  }

  public getOwnAnnotations(): Map<Type<any>, Annotation> {
    return this._annotations;
  }

  public getOwnAnnotationsByType<AnnotationType extends Annotation>(annotationClass: Type<AnnotationType>)
                                                                                                      : AnnotationType[] | undefined {
    if (0 === this._annotations.size) {
      return undefined;
    }
    const results: AnnotationType[] = [];
    for (const classType of Array.from(this._annotations.keys())) {
      if (classType === annotationClass) {
        results.push(<AnnotationType>this._annotations.get(classType));
      }
    }
    if (0 === results.length) {
      return undefined;
    }
    return results;
  }

  public getAnnotation<AnnotationType extends Annotation>(annotationClass: Type<AnnotationType>): AnnotationType | undefined {
    return this.getOwnAnnotation(annotationClass);
  }

  public getAnnotations(): Map<Type<any>, Annotation> {
    return this.getOwnAnnotations();
  }

  public getAnnotationsByType<AnnotationType extends Annotation>(annotationClass: Type<AnnotationType>)
                                                                                                      : AnnotationType[] | undefined {
    return this.getOwnAnnotationsByType(annotationClass);
  }
}
