// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef OTS_H_
#define OTS_H_

#include <stddef.h>
#include <cstdarg>
#include <cstddef>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <limits>
#include <map>

#include "opentype-sanitiser.h"

// arraysize borrowed from base/basictypes.h
template <typename T, size_t N>
char (&ArraySizeHelper(T (&array)[N]))[N];
#define arraysize(array) (sizeof(ArraySizeHelper(array)))

namespace ots {

#if !defined(OTS_DEBUG)
#define OTS_FAILURE() false
#else
#define OTS_FAILURE() \
  (\
    std::fprintf(stderr, "ERROR at %s:%d (%s)\n", \
                 __FILE__, __LINE__, __FUNCTION__) \
    && false\
  )
#endif

// All OTS_FAILURE_* macros ultimately evaluate to 'false', just like the original
// message-less OTS_FAILURE(), so that the current parser will return 'false' as
// its result (indicating a failure).

#if !defined(OTS_DEBUG)
#define OTS_MESSAGE_(level,otf_,...) \
  (otf_)->context->Message(level,__VA_ARGS__)
#else
#define OTS_MESSAGE_(level,otf_,...) \
  OTS_FAILURE(), \
  (otf_)->context->Message(level,__VA_ARGS__)
#endif

// Generate a simple message
#define OTS_FAILURE_MSG_(otf_,...) \
  (OTS_MESSAGE_(0,otf_,__VA_ARGS__), false)

#define OTS_WARNING_MSG_(otf_,...) \
  OTS_MESSAGE_(1,otf_,__VA_ARGS__)

// Generate a message with an associated table tag
#define OTS_FAILURE_MSG_TAG_(otf_,msg_,tag_) \
  (OTS_MESSAGE_(0,otf_,"%c%c%c%c: %s", OTS_UNTAG(tag_), msg_), false)

// Convenience macros for use in files that only handle a single table tag,
// defined as TABLE_NAME at the top of the file; the 'file' variable is
// expected to be the current OpenTypeFile pointer.
#define OTS_FAILURE_MSG(...) OTS_FAILURE_MSG_(font->file, TABLE_NAME ": " __VA_ARGS__)

#define OTS_WARNING(...) OTS_WARNING_MSG_(font->file, TABLE_NAME ": " __VA_ARGS__)

// -----------------------------------------------------------------------------
// Buffer helper class
//
// This class perform some trival buffer operations while checking for
// out-of-bounds errors. As a family they return false if anything is amiss,
// updating the current offset otherwise.
// -----------------------------------------------------------------------------
class Buffer {
 public:
  Buffer(const uint8_t *buf, size_t len)
      : buffer_(buf),
        length_(len),
        offset_(0) { }

  bool Skip(size_t n_bytes) {
    return Read(NULL, n_bytes);
  }

  bool Read(uint8_t *buf, size_t n_bytes) {
    if (n_bytes > 1024 * 1024 * 1024) {
      return OTS_FAILURE();
    }
    if ((offset_ + n_bytes > length_) ||
        (offset_ > length_ - n_bytes)) {
      return OTS_FAILURE();
    }
    if (buf) {
      std::memcpy(buf, buffer_ + offset_, n_bytes);
    }
    offset_ += n_bytes;
    return true;
  }

  inline bool ReadU8(uint8_t *value) {
    if (offset_ + 1 > length_) {
      return OTS_FAILURE();
    }
    *value = buffer_[offset_];
    ++offset_;
    return true;
  }

  bool ReadU16(uint16_t *value) {
    if (offset_ + 2 > length_) {
      return OTS_FAILURE();
    }
    std::memcpy(value, buffer_ + offset_, sizeof(uint16_t));
    *value = ntohs(*value);
    offset_ += 2;
    return true;
  }

  bool ReadS16(int16_t *value) {
    return ReadU16(reinterpret_cast<uint16_t*>(value));
  }

  bool ReadU24(uint32_t *value) {
    if (offset_ + 3 > length_) {
      return OTS_FAILURE();
    }
    *value = static_cast<uint32_t>(buffer_[offset_]) << 16 |
        static_cast<uint32_t>(buffer_[offset_ + 1]) << 8 |
        static_cast<uint32_t>(buffer_[offset_ + 2]);
    offset_ += 3;
    return true;
  }

  bool ReadU32(uint32_t *value) {
    if (offset_ + 4 > length_) {
      return OTS_FAILURE();
    }
    std::memcpy(value, buffer_ + offset_, sizeof(uint32_t));
    *value = ntohl(*value);
    offset_ += 4;
    return true;
  }

  bool ReadS32(int32_t *value) {
    return ReadU32(reinterpret_cast<uint32_t*>(value));
  }

  bool ReadR64(uint64_t *value) {
    if (offset_ + 8 > length_) {
      return OTS_FAILURE();
    }
    std::memcpy(value, buffer_ + offset_, sizeof(uint64_t));
    offset_ += 8;
    return true;
  }

  const uint8_t *buffer() const { return buffer_; }
  size_t offset() const { return offset_; }
  size_t length() const { return length_; }

  void set_offset(size_t newoffset) { offset_ = newoffset; }

 private:
  const uint8_t * const buffer_;
  const size_t length_;
  size_t offset_;
};

// Round a value up to the nearest multiple of 4. Don't round the value in the
// case that rounding up overflows.
template<typename T> T Round4(T value) {
  if (std::numeric_limits<T>::max() - value < 3) {
    return value;
  }
  return (value + 3) & ~3;
}

template<typename T> T Round2(T value) {
  if (value == std::numeric_limits<T>::max()) {
    return value;
  }
  return (value + 1) & ~1;
}

bool IsValidVersionTag(uint32_t tag);

#define FOR_EACH_TABLE_TYPE \
  F(cff, CFF) \
  F(cmap, CMAP) \
  F(cvt, CVT) \
  F(fpgm, FPGM) \
  F(gasp, GASP) \
  F(gdef, GDEF) \
  F(glyf, GLYF) \
  F(gpos, GPOS) \
  F(gsub, GSUB) \
  F(hdmx, HDMX) \
  F(head, HEAD) \
  F(hhea, HHEA) \
  F(hmtx, HMTX) \
  F(kern, KERN) \
  F(loca, LOCA) \
  F(ltsh, LTSH) \
  F(math, MATH) \
  F(maxp, MAXP) \
  F(name, NAME) \
  F(os2, OS2) \
  F(post, POST) \
  F(prep, PREP) \
  F(vdmx, VDMX) \
  F(vorg, VORG) \
  F(vhea, VHEA) \
  F(vmtx, VMTX)

#define F(name, capname) struct OpenType##capname;
FOR_EACH_TABLE_TYPE
#undef F

struct Font;
struct OpenTypeFile;

#define F(name, capname) \
bool ots_##name##_parse(Font *f, const uint8_t *d, size_t l); \
bool ots_##name##_should_serialise(Font *f); \
bool ots_##name##_serialise(OTSStream *s, Font *f); \
void ots_##name##_reuse(Font *f, Font *o);\
void ots_##name##_free(Font *f);
FOR_EACH_TABLE_TYPE
#undef F

struct Font {
  explicit Font(const OpenTypeFile *f)
      : file(f),
        version(0),
        num_tables(0),
        search_range(0),
        entry_selector(0),
        range_shift(0) {
#define F(name, capname) \
    name = NULL; \
    name##_reused = false;
    FOR_EACH_TABLE_TYPE
#undef F
  }

  ~Font() {
#define F(name, capname) \
    if (!name##_reused) {\
      ots_##name##_free(this); \
    }
    FOR_EACH_TABLE_TYPE
#undef F
  }

  const OpenTypeFile *file;

  uint32_t version;
  uint16_t num_tables;
  uint16_t search_range;
  uint16_t entry_selector;
  uint16_t range_shift;

#define F(name, capname) \
  OpenType##capname *name; \
  bool name##_reused;
FOR_EACH_TABLE_TYPE
#undef F
};

struct OutputTable {
  uint32_t tag;
  size_t offset;
  size_t length;
  uint32_t chksum;

  bool operator<(const OutputTable& other) const {
    return tag < other.tag;
  }
};

typedef std::map<uint32_t, std::pair<Font*, OutputTable> > TableMap;

struct OpenTypeFile {
  OTSContext *context;
  TableMap tables;
};

}  // namespace ots

#undef FOR_EACH_TABLE_TYPE

#endif  // OTS_H_
