import React from 'react'
import ChangeCase from 'change-case'
import capitalize from 'lodash/capitalize'
import cloneDeep from 'lodash/cloneDeep'
import sortBy from 'lodash/sortBy'
import pull from 'lodash/pull'
import Tools from './../app/assets/javascripts/teachable/tools'

$(document).ready(() => {
  if (window.location.pathname.indexOf("admin") > -1 && window.location.pathname.indexOf("preview") === -1) {
    Admin.initialize();
  }
});

type StringProps<T> = { [K in keyof T]: T[K] extends string ? K : never }[keyof T];

interface currentUserType {
  access?: number | string;
  id?: number;
  imageUrl?: string;
  name?: string;
}

let currentUser: currentUserType = {}

const Admin = {

  currentUser: currentUser,

  permissions: {},

  errors: {
    email: [
      "Email can't be blank",
      "Email is invalid",
      "Email has already been taken"
    ],
    name: [
      "Name can't be blank",
      "Name has already been taken"
    ],
    password: [
      "Password can't be blank",
      "Passwords do not match",
      "Password must be at least six characters"
    ],
    confirmNewPassword: [
      "Passwords do not match"
    ],
    charLimit: [
      "Limit can't be zero"
    ],
    duration: [
      "Duration is not a number",
      "Duration must be greater than or equal to 1"
    ],
    ctaButtonText: [
      "Cta button text can't be blank"
    ],
    ctaUrl: [
      "Cta url can't be blank"
    ],
    startTime: [
      "Start time is not a number",
      "Start time must be greater than or equal to 0"
    ],
    tagline: [
      "Tagline can't be blank"
    ],
    monthlyPrice: [
      "Monthly price can't be blank",
      "Monthly price is not a number",
      "Monthly price must be greater than or equal to 0",
      "Monthly price must be an integer"
    ],
    annualPrice: [
      "Annual price can't be blank",
      "Annual price is not a number",
      "Annual price must be greater than or equal to 0",
      "Annual price must be an integer"
    ],
    appMonthlyPlanId: [
      "App monthly plan can't be blank",
      "App monthly plan is not a number",
      "App monthly plan must be greater than or equal to 0",
      "App monthly plan must be an integer"
    ],
    appAnnualPlanId: [
      "App annual plan can't be blank",
      "App annual plan is not a number",
      "App annual plan must be greater than or equal to 0",
      "App annual plan must be an integer"
    ],
    includesText: [
      "Includes text can't be blank"
    ],
    url: [
      "Url can't be blank",
      "Url has already been taken",
      "Url is invalid"
    ],
    redirect: [
      "Redirect can't be blank",
      "Redirect is invalid",
      "Redirect can't be blank with live end date"
    ],
    titleText: [
      "Title text can't be blank"
    ],
    canonicalUrl: [
      "Canonical url can't be blank",
      "Canonical url is invalid (format: http(s)://teachable.com/URL_NAME)",
    ],
    title: [
      "Title can't be blank",
      "Title has already been taken"
    ],
    time: [
      "Time is not valid",
      "Live start date must be before webinar time(s)",
      "Live end date must be after webinar time(s)",
      "Time cannot be in the past"
    ],
    time2: [
      "Time2 is not valid",
      "Live start date must be before webinar time(s)",
      "Live end date must be after webinar time(s)"
    ],
    date: [
      "Date is not valid",
      "Date cannot be in the past"
    ],
    hostId: [
      "Host and inviter are not unique"
    ],
    inviterId: [
      "Host and inviter are not unique"
    ],
    text: [
      "Text can't be blank",
      "Text has already been taken"
    ],
    upgradeTimeframe: [
      "Upgrade timeframe and upgrade options not compatible",
      "Upgrade timeframe must be monthly for business"
    ],
    upgradeOptions: [
      "Upgrade timeframe and upgrade options not compatible"
    ],
    liveStartDate: [
      "Live start date is not valid",
      "Live start date must be before live end date",
      "Live start date must be before webinar time(s)"
    ],
    liveEndDate: [
      "Live end date is not valid",
      "Live start date must be before live end date",
      "Redirect can't be blank with live end date",
      "Live end date must be after webinar time(s)"
    ],
    description: [
      "Description can't be blank"
    ],
    slug: [
      "Slug can't be blank",
      "Slug only accepts letters, numbers and hyphens",
      "Slug has already been taken"
    ],
    destinationUrl: [
      "Destination url can't be blank",
      "Destination url is invalid"
    ],
    schedulableId: [
      "Schedulable can't be blank"
    ],
    externalLink: [
      "Redemption link can't be blank"
    ],
    uploadId: [
      "Redemption link can't be blank"
    ],
    landingPageSlug: [
      "Landing page slug can't be blank",
      "Landing page slug is invalid",
      "Landing page slug has already been taken"
    ],
    landingPageBackgroundImageId: [
      "Landing page background image can't be blank"
    ],
    landingPageLargeImageId: [
      "Landing page large image can't be blank"
    ],
    landingPageHeader: [
      "Landing page header can't be blank"
    ],
    landingPageButtonText: [
      "Landing page button text can't be blank"
    ],
    landingPageBulletHeader: [
      "Landing page bullet header can't be blank"
    ],
    landingPageBulletPoints: [
      "Landing page bullet points can't be blank"
    ],
    landingPageEventName: [
      "Landing page event name can't be blank"
    ],
    contentScore: [
      "Content score is not a number"
    ],
    bannerKey: [
      "Banner key can't be blank"
    ],
    "homepage - header": [
      "Homepage Header is mandatory",
    ],
    "homepage - subheader": [
      "Homepage Subheader is mandatory",
    ],
    "homepage - contentDownload1EventName": [
      "Homepage Event 1 is mandatory",
    ],
    "homepage - contentDownload1WorkflowId": [
      "Homepage Workflow ID 1 is mandatory",
      "Homepage Workflow ID 1 is not a number",
    ],
    "homepage - contentDownload2EventName": [
      "Homepage Event 2 is mandatory",
    ],
    "homepage - contentDownload2WorkflowId": [
      "Homepage Workflow ID 2 is mandatory",
      "Homepage Workflow ID 2 is not a number",
    ],
    "resources - header": [
      "Resources Header is mandatory",
    ],
    "resources - subheader": [
      "Resources Subheader is mandatory",
    ],
    "resources - featuredContentDownloadEventName": [
      "Resources Featured CU Event Name is mandatory",
    ],
    "resources - featuredContentDownloadWorkflowId": [
      "Resources Featured CU Workflow ID is mandatory",
      "Resources Featured CU Workflow ID is not a number",
    ],
    "category1 - name": [
      "Category 1 Name can't be blank",
    ],
    "category1 - url": [
      "Category 1 Url can't be blank",
    ],
    "category1 - titleText": [
      "Category 1 Title text can't be blank",
    ],
    "category1 - description": [
      "Category 1 Description can't be blank",
    ],
    "category1 - header": [
      "Category 1 Header can't be blank",
    ],
    "category1 - subheader": [
      "Category 1 Subheader can't be blank"
    ],
    "category1 - contentDownload1Id": [
      "Category 1 Content Download 1 can't be blank"
    ],
    "category1 - contentDownload1EventName": [
      "Category 1 Event name 1 can't be blank"
    ],
    "category1 - contentDownload1WorkflowId": [
      "Category 1 Workflow ID 1 can't be blank",
      "Category 1 Workflow ID 1 is not a number"
    ],
    "category1 - contentDownload2Id": [
      "Category 1 Content Download 2 can't be blank"
    ],
    "category1 - contentDownload2EventName": [
      "Category 1 Event name 2 can't be blank"
    ],
    "category1 - contentDownload2WorkflowId": [
      "Category 1 Workflow ID 2 can't be blank",
      "Category 1 Workflow ID 2 is not a number"
    ],
    "category2 - name": [
      "Category 2 Name can't be blank",
    ],
    "category2 - url": [
      "Category 2 Url can't be blank",
    ],
    "category2 - titleText": [
      "Category 2 Title text can't be blank",
    ],
    "category2 - description": [
      "Category 2 Description can't be blank",
    ],
    "category2 - header": [
      "Category 2 Header can't be blank",
    ],
    "category2 - subheader": [
      "Category 2 Subheader can't be blank"
    ],
    "category2 - contentDownload1Id": [
      "Category 2 Content Download 1 can't be blank"
    ],
    "category2 - contentDownload1EventName": [
      "Category 2 Event name 1 can't be blank"
    ],
    "category2 - contentDownload1WorkflowId": [
      "Category 2 Workflow ID 1 can't be blank",
      "Category 2 Workflow ID 1 is not a number"
    ],
    "category2 - contentDownload2Id": [
      "Category 2 Content Download 2 can't be blank"
    ],
    "category2 - contentDownload2EventName": [
      "Category 2 Event name 2 can't be blank"
    ],
    "category2 - contentDownload2WorkflowId": [
      "Category 2 Workflow ID 2 can't be blank",
      "Category 2 Workflow ID 2 is not a number"
    ],
    "category3 - name": [
      "Category 3 Name can't be blank",
    ],
    "category3 - url": [
      "Category 3 Url can't be blank",
    ],
    "category3 - titleText": [
      "Category 3 Title text can't be blank",
    ],
    "category3 - description": [
      "Category 3 Description can't be blank",
    ],
    "category3 - header": [
      "Category 3 Header can't be blank",
    ],
    "category3 - subheader": [
      "Category 3 Subheader can't be blank"
    ],
    "category3 - contentDownload1Id": [
      "Category 3 Content Download 1 can't be blank"
    ],
    "category3 - contentDownload1EventName": [
      "Category 3 Event name 1 can't be blank"
    ],
    "category3 - contentDownload1WorkflowId": [
      "Category 3 Workflow ID 1 can't be blank",
      "Category 3 Workflow ID 1 is not a number"
    ],
    "category3 - contentDownload2Id": [
      "Category 3 Content Download 2 can't be blank"
    ],
    "category3 - contentDownload2EventName": [
      "Category 3 Event name 2 can't be blank"
    ],
    "category3 - contentDownload2WorkflowId": [
      "Category 3 Workflow ID 2 can't be blank",
      "Category 3 Workflow ID 2 is not a number"
    ]
  },

  initialize: function () {
    $.fn.matchHeight._maintainScroll = true;
    Admin.storeCurrentUser();
    Admin.storePermissions();
    Admin.highlightCurrentPageInMenu();
    window.filepicker.setKey("AtM7HNKzQZ6u2HxwJF1Jiz");
  },

  addImageToBlog: function(callback) {
    window.filepicker.pick({
      storeTo: {
        location: 's3'
      },
      mimetype: 'image/*', /* Images only */
      maxSize: 1024 * 1024 * 5, /* 5mb */
      services: ['COMPUTER']
    }, callback);
  },

  calculateFieldWidth: function(field) {
    var pixels = 10 * field.charLimit;
    return Math.ceil(pixels / 100) * 100;
  },

  canIDrop: function($e) {
    var draggedIndex = +$e[0].dataset.index;
    var dropZoneIndex = +this.dataset.index;
    var difference = Math.abs(draggedIndex - dropZoneIndex);
    if (difference >= 2) {
      return true;
    } else if (difference == 1 && draggedIndex < dropZoneIndex) {
      return true;
    }
    return false;
  },

  changeField: function(changeFieldArgs, event) {
    var key = event.target.dataset.field;
    var thing = event.target.dataset.entity || changeFieldArgs.thing;
    var newThing = cloneDeep(this.state[thing]);
    if (changeFieldArgs.thing == "object" || changeFieldArgs.thing == "pagePartial") {
      if (!event.target.dataset.headerstyle) {
        Admin.removeDynamicFieldError(changeFieldArgs.errorsArray, event.target.dataset.fieldname);
      }
    } else {
      Admin.removeFieldError(changeFieldArgs.errorsArray, key);
    }

    if ($(event.target).is('select')) {
      if (event.target.dataset.headerstyle === "true") {
        newThing[key + "_style"] = +event.target.value;
      } else {
        newThing[key] = Tools.convertTrueFalseFromStringToBoolean(event.target.value);
        if (key === "fieldType") {
          if (event.target.value === "object") {
            newThing.objectType = this.props.parentTemplates()[0].name
          } else {
            newThing.objectType = "";
          }
        }
      }
    } else {
      let value = event.target.value;
      if (changeFieldArgs.autoFormat) {
        value = changeFieldArgs.autoFormat(value);
      }
      newThing[key] = value;
    }

    if (changeFieldArgs.beforeSave) {
      changeFieldArgs.beforeSave.call(this, newThing, key, event.target.value);
    }

    this.setState({ [thing]: newThing, justSaved: false }, function() {
      if (changeFieldArgs.changesFunction) {
        var changesToSave = changeFieldArgs.changesFunction.call();
        this.setState({ changesToSave: changesToSave }, function() {
          if (changeFieldArgs.callback) {
            changeFieldArgs.callback.call(this, this.state[thing]);
          }
        });
      } else if (changeFieldArgs.callback) {
        changeFieldArgs.callback.call(this, this.state[thing]);
      }
    });
  },

  changePathName: function(url) {
    window.location.pathname = url;
  },

  changeSearchText: function(event) {
    this.setState({
      searchText: event.target.value
    });
  },

  changeState(property, value) {
    this.setState({
      [property]: value
    });
  },

  changeStateToTarget(property, e) {
    this.setState({
      [property]: e.target.value
    });
  },

  clickArrayCheckbox(args, option) {
    let value = option[args.optionValue];
    let array = this.state[args.selectedOptionsArrayName];
    if (array.indexOf(value) > -1) {
      pull(array, value);
    } else {
      array.push(value);
    }
    this.setState({
      [args.selectedOptionsArrayName]: array
    }, () => {
      if (this.changeFieldArgs) {
        let changeFieldArgs = this.changeFieldArgs();
        let changesToSave = changeFieldArgs.changesFunction.call();
        this.setState({ changesToSave });
      }
    });
  },

  clickDelete(callback) {
    this.setState({
      deleteModalOpen: false,
      fetching: true
    });
    let urlSections = window.location.pathname.split('/');
    this.props.deleteEntity({
      directory: urlSections[urlSections.length - 2],
      id: urlSections[urlSections.length - 1],
      callback
    });
  },

  clickHeader(property) {
    var key = this.state.searchProperty ? 'searchProperty' : 'searchColumn';
    this.setState({
      [key]: property
    });
  },

  clickMakeReusable(args) {
    this.props.updateEntity({
      id: args.uploadId,
      directory: 'uploads',
      entityName: 'upload',
      entity: Object.assign(args.image, { reusable: true }),
    }).then(() => {
      let images = this.state.images;
      let newImage = images[args.field.id];
      newImage['reusability'] = true;
      this.setState({
        images
      });
    });
  },

  clickNew() {
    this.setState({
      newEntityModalOpen: true
    });
  },

  clickTab(e) {
    var tab = e.target.innerText;
    if (this.state.tab !== tab) {
      this.setState({
        tab: tab
      });
    }
  },

  clickRemoveUpload(args, imageUrlProperty) {
    let entity = this.state[args.entity];
    entity[args.property] = "";
    entity[imageUrlProperty] = "";
    this.setState({
      fetching: false,
      [args.entity]: entity
    }, () => {
      if (this.changeFieldArgs().changesFunction) {
        let changesToSave = this.changeFieldArgs().changesFunction.call();
        this.setState({
          changesToSave
        });
      }
    });
  },

  clickUploadImage(args, imageUrlProperty) {
    window.filepicker.pick({
      storeTo: {
        location: 's3'
      },
      mimetype: 'image/*',
      maxSize: 1024 * 1024 * 5,
      services: ['COMPUTER'],
      debug: window.FILEPICKER_DEBUG
    }, (response) => {
      let entity = this.state[args.entityName];
      let newArgs = Object.assign(args, { entity });
      if (imageUrlProperty) {
        entity[imageUrlProperty] = response.url;
      } else if (args.isInstance) {
        let images = this.state.images;
        images[args.property].url = response.url;
        newArgs.uploadsList = images;
      }
      if (args.createImageRecord) {
        this.setState({
          fetching: true
        });
        this.props.createEntity({
          directory: "uploads",
          entityName: "upload",
          entity: {
            uploadType: "image",
            category: args.imageCategory || "(none)",
            url: response.url,
            s3_url: response.key,
            uploadedBy: Admin.currentUser.id
          }
        }).then(() => {
          entity[args.property] = this.props.upload.id;
          Admin.updateStateAfterImageOrFileUpload.call(this, newArgs);
        });
      } else {
        Admin.updateStateAfterImageOrFileUpload.call(this, newArgs);
      }
    });
  },

  closeDeleteModal: function() {
    this.setState({
      deleteModalOpen: false
    });
  },

  closeDeleteModalAndFetch: function() {
    this.setState({
      deleteModalOpen: false,
      fetching: true
    });
  },

  closeModals() {
    let keys = Object.keys(this.state).filter((key) => {
      return key.slice(-9) === 'ModalOpen';
    });
    let result = {};
    keys.forEach((key) => {
      result[key] = false
    });
    this.setState(result);
  },

  commonSort: function(object) {
    let property = object[this.state.searchProperty];
    if (["time"].indexOf(this.state.searchProperty) > -1) {
      return new Date(property.replace('-',''));
    } else if (typeof property === "string" || property instanceof String) {
      return property.toLowerCase();
    } else if (typeof property == "boolean") {
      return property.toString().toLowerCase();
    } else {
      return property;
    }
  },

  convertTemplateFieldsToSnakeCase: function(fields) {
    return fields.map(function(field) {
      return({
        char_limit: field.charLimit,
        field_type: field.fieldType,
        id: field.id,
        mandatory: field.mandatory,
        menu_options: field.menuOptions,
        name: field.name,
        object_type: field.objectType,
        description: field.description
      });
    });
  },

  currentUserProfile: function() {
    return Admin.currentUser.id.toString() == window.location.pathname.split("/")[3];
  },

  deleteEntity: function() {
    this.setState({
      fetching: true
    });
    let urlSections = window.location.pathname.split('/');
    this.props.deleteEntity({
      directory: urlSections[urlSections.length - 2],
      id: urlSections[urlSections.length - 1]
    });
  },

  destroyNiceSelect: function(selector) {
    var $dropDowns = $(selector);
    $dropDowns.niceSelect('destroy');
  },

  errorClass: function(stateErrors, fieldErrors, ignoreErrors = false) {
    if (ignoreErrors) {
      return "";
    }

    for (let i = 0; i < fieldErrors.length; i++) {
      if (stateErrors.indexOf(fieldErrors[i]) > -1) {
        return "error";
      }
    }
    return "";
  },

  filterSearchText<T>(inputArray: T[], searchText: string, property: StringProps<T>) {
    if (searchText !== '') {
      let re = new RegExp(searchText, 'i');
      return inputArray.filter(({ [property]: value }) =>  typeof value === 'string' && re.test(value));
    }
    return inputArray;
  },

  formatDateString: function(dateString) {
    return dateString.replace(' -', '');
  },

  getColumnHeader(args) {
    return args.columnHeader || ChangeCase.titleCase(args.property).replace(/\sId$/, ' ID');
  },

  highlightCurrentPageInMenu: function() {
    $("#admin-sidebar-body li a").each(function() {
      if (this.getAttribute("href") == window.location.pathname) {
        this.classList.add("highlight");
      };
    })
  },

  hyphenFormat(input) {
    return input.toLowerCase().split(' ').join('-');
  },

  hyphenUpperCaseFormat(input) {
    return input.toUpperCase().split(' ').join('-');
  },

  isValidDate(date) {
    return date.toString() !== 'Invalid Date';
  },

  matchColumnHeight() {
    $('.match-height-layout').matchHeight();
  },

  newEntityModalStyles(modalDimensions, rows) {
    let height;
    if (rows) {
      height = 121 + (rows * 119);
    } else {
      height = modalDimensions.height || 240;
    }
    return {
      overlay: {
        background: 'rgba(0, 0, 0, 0.50)'
      },
      content: {
        background: 'white',
        padding: 0,
        margin: 'auto',
        maxWidth: modalDimensions.width || 1000,
        height: height
      }
    }
  },

  redirect: function(directory, id) {
    window.location.pathname = directory + "/" + id;
  },

  removeDynamicFieldError: function(errorsArray, dynamicFieldName) {
    pull(errorsArray, capitalize(dynamicFieldName) + ' is mandatory');
    pull(errorsArray, ' ' + dynamicFieldName + ' is not a number');
  },

  removeFieldError: function(errorsArray, fieldName) {
    if (Admin.errors[fieldName]) {
      if (!errorsArray) {
        console.log("no errors array!!!");
      }
      Admin.errors[fieldName].forEach((message) => {
        pull(errorsArray, message);
      });
    }
  },

  renderBooleanDropDown(args) {
    let columnHeader = Admin.getColumnHeader(args);
    if (args.readOnly) {
      let letter = Tools.convertBooleanToTFString(this.state[args.entity][args.property]);
      args.value = { t: 'Yes', f: 'No' }[letter];
      return Admin.renderField.call(this, args);
    } else {
      return(
        <div className={ `col-xs-${args.columnWidth || '2'} boolean-dropdown` }>
          <h2 className={ args.fieldInfo ? "has-subheader" : "" }>{ columnHeader }</h2>
          { Admin.renderFieldInfo(args) }
          <select onChange={ () => {} } data-entity={ args.entity } data-field={ args.property } value={ Tools.convertBooleanToTFString(this.state[args.entity][args.property]) }>
            <option value="t">Yes</option>
            <option value="f">No</option>
          </select>
          { Admin.renderDropdownFieldError(this.state.errors, Admin.errors[args.property] || []) }
        </div>
      );
    }
  },

  renderCheckbox(args) {
    let columnHeader = Admin.getColumnHeader(args);
    if (args.hidden) {
      return <div className={ `col-xs-${args.columnWidth}` }></div>;
    } else {
      return(
        <div className={ `col-xs-${args.columnWidth}` }>
          <input id={ `${args.entity}-${args.property}` } className="checkbox" type="checkbox" onChange={ Admin.changeField.bind(this, this.changeFieldArgs()) } checked={ this.state[args.entity][args.property] || false } data-entity={ args.entity } data-field={ args.property } /><label className="checkbox" htmlFor={ `${args.entity}-${args.property}` }>{ columnHeader }</label>
          { Admin.renderFieldError(this.state.errors, Admin.errors[args.property] || []) }
        </div>
      );
    }
  },

  renderArrayCheckboxes(args) {
    let columnHeader = Admin.getColumnHeader(args);
    if (args.hidden) {
      return <div className={ `col-xs-${args.columnWidth}` }></div>;
    } else {
      return(
        <div className={ `array-checkboxes col-xs-${args.columnWidth}` }>
          <h2 className={ args.fieldInfo ? "has-subheader" : "" }>{ columnHeader }</h2>
          { Admin.renderFieldInfo(args) }
          { this.state[args.optionsArrayName].map((option, index) => {
            return(
              <div key={ index }>
                <input id={ ChangeCase.hyphenCase(option[args.optionLabel]) } type="checkbox" className="checkbox" onChange={ Admin.clickArrayCheckbox.bind(this, args, option) } checked={ this.state[args.selectedOptionsArrayName].indexOf(option[args.optionValue]) > -1 } />
                <label className="checkbox" htmlFor={ ChangeCase.hyphenCase(option[args.optionLabel]) }>{ option[args.optionLabel] }</label>
              </div>
            );
          }) }
        </div>
      );
    }
  },

  renderDetailsButtons() {
    return(
      <div className="row">
        <div className="col-xs-12">
          <a className={ "orange-button standard-button-width btn" + Admin.renderDisabledButtonClass(this.state.fetching || !this.state.changesToSave) } onClick={ this.clickSave.bind(this) }>
            { Admin.saveButtonText.call(this) }
          </a>
          <a className={ "delete-button " + Admin.renderDisabledButtonClass(this.state.fetching) } onClick={ Admin.changeState.bind(this, "deleteModalOpen", true) }>
            Delete
          </a>
        </div>
      </div>
    );
  },

  renderDisabledButtonClass: function(condition) {
    return condition ? ' disabled' : '';
  },

  renderDropDown(args) {

    function renderNoneOption(args) {
      if (args.includeBlank) {
        return(
          <option key={ -1 } value="">(None)</option>
        );
      } else {
        return null;
      }
    }

    function renderOptions(args) {
      if (args.boolean) {
        return([
          <option key={ 0 } value="t">Yes</option>,
          <option key={ 1 } value="f">No</option>
        ]);
      } else {
        let options;
        if (args.options) {
          options = args.doNotAlphabetize ? args.options : sortBy(args.options, args.optionLabel);
        } else {
          options = args.doNotAlphabetize ? this.state[optionsArrayName] : sortBy(this.state[optionsArrayName], args.optionLabel);
        }
        { return options.map((option, index) => {
          return(
            <option key={ index } value={ option[args.optionValue] }>{ (args.skipChangeCase ? option[args.optionLabel] : ChangeCase.titleCase(option[args.optionLabel])) }</option>
          );
        }) }
      }
    }

    let columnHeader = Admin.getColumnHeader(args);
    let optionsArrayName = args.optionsArrayName || `${args.property}s`;
    let limitClass = (args.limit ? ` dropdown-limit-${args.limit}` : '');
    if (args.hidden || (!args.boolean && (!args.options && !this.state[optionsArrayName]))) {
      return null;
    } else if (args.readOnly) {
      let value = this.state[args.entity][args.property];
      let optionsArray = this.state[optionsArrayName];
      if (value && optionsArray.length > 0) { // <-- make sure data is loaded from server
        let option = this.state[optionsArrayName].find(({[args.optionValue]: v}) => v === value);
        if (option) {
          args.value = (args.skipChangeCase ? option[args.optionLabel] : ChangeCase.titleCase(option[args.optionLabel]));
        }
      }
      return Admin.renderField.call(this, args);
    } else {
      return(
        <div className={ `col-xs-${args.columnWidth}${limitClass}` }>
          <h2 className={ args.fieldInfo ? "has-subheader" : "" }>{ columnHeader }</h2>
          { Admin.renderFieldInfo(args) }
          <select className={ Admin.errorClass(this.state.errors, Admin.errors[args.property] || []) } onChange={ Admin.changeField.bind(this, this.changeFieldArgs()) } data-entity={ args.entity } data-field={ args.property } value={ Tools.convertBooleanToTFString(this.state[args.entity][args.property]) }>
            { renderNoneOption(args) }
            { renderOptions.call(this, args) }
          </select>
          { Admin.renderDropdownFieldError(this.state.errors, Admin.errors[args.property] || []) }
        </div>
      );
    }
  },

  renderDropdownFieldError(stateErrors, fieldErrors) {
    for (var i = 0; i < fieldErrors.length; i++) {
      if (stateErrors.indexOf(fieldErrors[i]) > -1) {
        return(
          React.createElement("div", { className: "yes-dropdown-field-error" }, fieldErrors[i])
        );
      }
    }
    return(
      React.createElement("div", { className: "no-dropdown-field-error" })
    );
  },

  renderNumberField(args) {
    let columnHeader = Admin.getColumnHeader(args);
    let errorsArray = args.errorsArray || this.state.errors;
    let newChangeFieldArgs;
    if (args.autoFormat) {
      newChangeFieldArgs = Object.assign({}, this.changeFieldArgs()); // shallow copy to keep reference to errors array
      newChangeFieldArgs.autoFormat = args.autoFormat;
    }
    if (args.hidden) {
      return null;
    } else {
      return(
        <div className={ `col-xs-${args.columnWidth}` }>
          <h2 className={ args.fieldInfo ? "has-subheader" : "" }>{ columnHeader }</h2>
          { Admin.renderFieldInfo(args) }
          <input className={ Admin.errorClass(errorsArray, Admin.errors[args.property] || []) } onChange={ Admin.changeField.bind(this, newChangeFieldArgs || this.changeFieldArgs()) } value={ args.value || this.state[args.entity][args.property] || "" } data-entity={ args.entity } data-field={ args.property } placeholder={ args.placeholder } readOnly={ args.readOnly } type="number" min={args.max && args.min || 0} max={args.max || null} />
          { Admin.renderFieldError(errorsArray, Admin.errors[args.property] || []) }
        </div>
      );
    }
  },

  renderField(args) {
    let columnHeader = Admin.getColumnHeader(args);
    let errorsArray = args.errorsArray || this.state.errors;
    let newChangeFieldArgs;
    if (args.autoFormat) {
      newChangeFieldArgs = Object.assign({}, this.changeFieldArgs()); // shallow copy to keep reference to errors array
      newChangeFieldArgs.autoFormat = args.autoFormat;
    }
    if (args.hidden) {
      return null;
    } else {
      return(
        <div className={ `col-xs-${args.columnWidth}` }>
          <h2 className={ args.fieldInfo ? "has-subheader" : "" }>{ columnHeader }</h2>
          { Admin.renderFieldInfo(args) }
          <input className={ Admin.errorClass(errorsArray, Admin.errors[args.property] || []) } onChange={ Admin.changeField.bind(this, newChangeFieldArgs || this.changeFieldArgs()) } value={ args.value || this.state[args.entity][args.property] || "" } data-entity={ args.entity } data-field={ args.property } placeholder={ args.placeholder } readOnly={ args.readOnly } />
          { Admin.renderFieldError(errorsArray, Admin.errors[args.property] || []) }
        </div>
      );
    }
  },

  renderFieldError: function(stateErrors, fieldErrors, ignoreErrors = false, modal = false) {
    if (ignoreErrors) {
      return(
        <div className="no-field-error"></div>
      );
    }
    for (let i = 0; i < fieldErrors.length; i++) {
      if (stateErrors.indexOf(fieldErrors[i]) > -1) {
        if (modal) {
          return(
            <div className="yes-modal-field-error">{ fieldErrors[i] }</div>
          );
        } else {
          return(
            <div className="yes-field-error">{ fieldErrors[i] }</div>
          );
        }
      }
    }
    if (modal) {
      return(
        <div className="no-modal-field-error"></div>
      );
    } else {
      return(
        <div className="no-field-error"></div>
      );
    }
  },

  renderFieldInfo: function(args) {
    if (args.fieldInfo) {
      return(
        <p className="field-info">{ args.fieldInfo }</p>
      );
    } else {
      return null;
    }
  },

  renderGrayedOut: function(fetching) {
    if (fetching) {
      return(
        <div className="grayed-out"></div>
      );
    } else {
      return null;
    }
  },

  renderFileField: function(args) {
    args['entityName'] = args['entity'];
    let columnHeader = Admin.getColumnHeader(args);
    let fileUrlProperty = args.fileUrlProperty || args.property;
    if (args.hidden) {
      return null;
    } else {
      return(
        <div className={ `col-xs-${args.columnWidth} file-field` }>
          <h2 className={ args.fieldInfo ? "has-subheader" : "" }>{ columnHeader }</h2>
          { Admin.renderFieldInfo(args) }
          <p className={ Admin.errorClass(this.state.errors, Admin.errors[args.property] || []) }>{ this.state[args.entity][fileUrlProperty] }</p>
          <a className="change-upload" onClick={ Admin.clickUploadImage.bind(this, args, fileUrlProperty) }>Upload File</a>
          <a className="remove-upload" onClick={ Admin.clickRemoveUpload.bind(this, args, fileUrlProperty) }>Remove File</a>
          { Admin.renderFieldError(this.state.errors, Admin.errors[args.property] || []) }
        </div>
      );
    }
  },

  renderImageField: function(args) {

    function renderImageArea(args, imageUrlProperty) {
      if (this.state[args.entity][imageUrlProperty] === '') {
        return(
          <div>
            <div className={ `image-field-image ${Admin.errorClass(this.state.errors, Admin.errors[args.property] || [])}` }></div>
            <a className="change-upload" onClick={ Admin.clickUploadImage.bind(this, args, imageUrlProperty) }>
              Upload Image
            </a>
            { Admin.renderFieldError(this.state.errors, Admin.errors[args.property] || []) }
          </div>
        );
      } else {
        return(
          <div>
            <div className={ `image-field-image ${Admin.errorClass(this.state.errors, Admin.errors[args.property] || [])}` } style={ { 'backgroundImage': ('url(' + this.state[args.entity][imageUrlProperty] + ')') } }></div>
            <a className="change-upload" onClick={ Admin.clickUploadImage.bind(this, args, imageUrlProperty) }>
              Change Image
            </a>
            <a className="remove-upload" onClick={ Admin.clickRemoveUpload.bind(this, args, imageUrlProperty) } >
              Remove Image
            </a>
            { Admin.renderFieldError(this.state.errors, Admin.errors[args.property] || []) }
          </div>
        );
      }
    }

    args['entityName'] = args['entity'];
    let columnHeader = Admin.getColumnHeader(args);
    let imageUrlProperty = args.imageUrlProperty || args.property;

    if (args.hidden) {
      return null;
    } else {
      return(
        <div className={ `col-xs-${args.columnWidth}` }>
          <h2 className={ args.fieldInfo ? "has-subheader" : "" }>{ columnHeader }</h2>
          { Admin.renderFieldInfo(args) }
          { renderImageArea.call(this, args, imageUrlProperty) }
        </div>
      );
    }
  },

  renderModalFieldError: function(stateErrors, fieldErrors) {
    for (let i = 0; i < fieldErrors.length; i++) {
      if (stateErrors.indexOf(fieldErrors[i]) > -1) {
        return(
          <div className="yes-modal-field-error">{ fieldErrors[i] }</div>
        );
      }
    }
    return(
      <div className="no-modal-field-error"></div>
    );
  },

  renderSpinner: function(condition = true) {
    if (condition) {
      return(
        <div className="spinner"></div>
      );
    } else {
      return null;
    }
  },

  renderTextBox(args) {
    let columnHeader = Admin.getColumnHeader(args);
    return(
      <div className={ `col-xs-${args.columnWidth}` }>
        <h2 className={ args.fieldInfo ? "has-subheader" : "" }>{ columnHeader }</h2>
        { Admin.renderFieldInfo(args) }
        <textarea rows={ args.rows } className={ Admin.errorClass(this.state.errors, Admin.errors[args.property] || []) } onChange={ Admin.changeField.bind(this, this.changeFieldArgs()) } value={ this.state[args.entity][args.property] || "" } data-entity={ args.entity } data-field={ args.property }></textarea>
        { Admin.renderFieldError(this.state.errors, Admin.errors[args.property] || []) }
      </div>
    );
  },

  renderTopTabs(entityNamePlural, tabs) {
    if (this.state[entityNamePlural].length > 0) {
      return(
        <div className="tabs-row">
          { tabs.map((tab) => {
            return(
              <div key={ tab } className={ "tab" + (this.state.tab === tab ? " selected" : "") } onClick={ Admin.clickTab.bind(this) }>
                { tab }
              </div>
            );
          }) }
        </div>
      );
    } else {
      return null;
    }
  },

  resetNiceSelect: function(obj) {
    var $dropDowns = $(obj.selector);
    $dropDowns.niceSelect('destroy');
    $dropDowns.unbind('change');
    if (obj.removeOrphanedDivs) {
      $('div.nice-select').remove(); // destroy call above won't remove debris if underlying select tags are gone
    }
    $dropDowns.niceSelect().on('change', obj.func);
    if (obj.adjustWidthSelector) {
      this.setNiceSelectWidth(obj.adjustWidthSelector);
    }
  },

  saveButtonText() {
    return this.state.changesToSave ? 'Save' : (this.state.justSaved ? 'Saved' : 'No Changes');
  },

  setNiceSelectWidth: function(adjustWidthSelector) {
    const minWidth = 150;
    let $selectsList = $(adjustWidthSelector);
    $selectsList.each((index, niceSelectDiv) => {
      let $niceSelectDiv = $(niceSelectDiv);
      let $ul = $(niceSelectDiv.children[1]);
      $ul.css('width', 'auto'); // change width to auto temporarily so we can determine the natural width of the dropdown menu
      let width = parseInt($ul.css('width').slice(0, -2));
      width = width > minWidth ? width : minWidth;
      $niceSelectDiv.css('width', width);
      $ul.css('width', '100%');
    });
  },

  setUpNiceSelect: function(obj) {
    var $dropDowns = $(obj.selector);
    $dropDowns.niceSelect().on('change', obj.func);
    if (obj.adjustWidthSelector) {
      this.setNiceSelectWidth(obj.adjustWidthSelector);
    }
  },

  sortClass: function(property) {
    var state = this.state.searchProperty || this.state.searchColumn;
    return state === property ? "sort-header-active" : "sort-header-inactive";
  },

  storeCurrentUser: function() {
    var $currentUserDiv = $('#current-user');
    $currentUserDiv.children().each(function(index, childDiv) {
      Admin.currentUser[childDiv.getAttribute("id")] = childDiv.innerHTML;
    });
  },

  storePermissions: function() {
    var $permissionsDiv = $('#permissions');
    $permissionsDiv.children().each(function(index, childDiv) {
      Admin.permissions[childDiv.getAttribute("id")] = childDiv.innerHTML;
    });
  },

  updateStateAfterImageOrFileUpload(args) {
    const errorsArray = this.changeFieldArgs().errorsArray;
    let newState = {
      fetching: false,
    };
    if (args.isInstance) {
      newState[args.isFile ? 'files' : 'images'] = args.uploadsList;
      newState['object'] = args.entity;
      Admin.removeDynamicFieldError(errorsArray, args.field.name);
    } else {
      Admin.removeFieldError(errorsArray, args.property);
      newState[args.entityName] = args.entity;
    }
    this.setState(newState, () => {
      if (this.changeFieldArgs().changesFunction) {
        let changesToSave = this.changeFieldArgs().changesFunction.call();
        this.setState({
          changesToSave
        });
      }
    });
  }
}

export default Admin;
