"""Output or upload a TestRun proto for mfg-inspector.com

MULTIDIM_JSON schema:
{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "title": "Multi-dimensional test parameter",
  "type": "object",
  "properties": {
    "outcome": {"enum": ["PASS", "FAIL", "ERROR"]},
    "name": {"type": "string"},
    "dimensions": {
      "type": array,
      "minItems": 1,
      "items": {
        "type": "object",
        "properties": {
          "uom_code": {"type": "string"},
          "uom_suffix": {"type": "string"}
        }
      }
    },
    "values": {
      "type": "array",
      "items": {}
    }
  }
}
"""

import json
import logging
import numbers
import os
import threading
import zlib
try:
  from past.types import unicode
except ImportError:
  pass

import httplib2
import oauth2client.client

from openhtf.core import measurements
from openhtf.core import test_record
from openhtf.output import callbacks
from openhtf.output.callbacks import json_factory
from openhtf.output.proto import guzzle_pb2
from openhtf.output.proto import test_runs_pb2
from openhtf.util import validators
import six

# pylint: disable=no-member
MIMETYPE_MAP = {
    'image/jpeg': test_runs_pb2.JPG,
    'image/png': test_runs_pb2.PNG,
    'audio/x-wav': test_runs_pb2.WAV,
    'text/plain': test_runs_pb2.TEXT_UTF8,
    'image/tiff': test_runs_pb2.TIFF,
    'video/mp4': test_runs_pb2.MP4,
}
OUTCOME_MAP = {
    test_record.Outcome.ERROR: test_runs_pb2.ERROR,
    test_record.Outcome.FAIL: test_runs_pb2.FAIL,
    test_record.Outcome.PASS: test_runs_pb2.PASS,
    test_record.Outcome.TIMEOUT: test_runs_pb2.ERROR,
    test_record.Outcome.ABORTED: test_runs_pb2.ERROR,
}

UOM_CODE_MAP = {
    u.GetOptions().Extensions[
        test_runs_pb2.uom_code]: num
    for num, u in six.iteritems(
        test_runs_pb2.Units.UnitCode.DESCRIPTOR.values_by_number)
}
# pylint: enable=no-member

# Control how many flattened parameters we'll output per multidimensional
# measurement.
MAX_PARAMS_PER_MEASUREMENT = 100


class UploadFailedError(Exception):
  """Raised when an upload to mfg-inspector fails."""


class InvalidTestRunError(Exception):
  """Raised if test run is invalid."""


# pylint: disable=invalid-name
def _populate_header(record, testrun):
  """Populate header-like info in testrun from record.

  Mostly obvious, some stuff comes from metadata, see docstring of
  _test_run_from_test_record for details.
  """
  testrun.dut_serial = record.dut_id
  testrun.tester_name = record.station_id
  if 'test_name' in record.metadata:
    testrun.test_info.name = record.metadata['test_name']
  else:
    # Default to copying tester_name into test_info.name.
    testrun.test_info.name = record.station_id
  if 'test_description' in record.metadata:
    testrun.test_info.description = record.metadata['test_description']
  if 'test_version' in record.metadata:
    testrun.test_info.version_string = record.metadata['test_version']
  testrun.test_status = OUTCOME_MAP[record.outcome]
  testrun.start_time_millis = record.start_time_millis
  testrun.end_time_millis = record.end_time_millis
  if 'run_name' in record.metadata:
    testrun.run_name = record.metadata['run_name']
  for details in record.outcome_details:
    testrun_code = testrun.failure_codes.add()
    testrun_code.code = details.code
    testrun_code.details = details.description
  for phase in record.phases:
    testrun_phase = testrun.phases.add()
    testrun_phase.name = phase.name
    testrun_phase.description = phase.codeinfo.sourcecode
    testrun_phase.timing.start_time_millis = phase.start_time_millis
    testrun_phase.timing.end_time_millis = phase.end_time_millis