/*
 * Copyright (c) 2016 Advanced Micro Devices, Inc.
 * All rights reserved.
 *
 * For use for simulation and test purposes only
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Brandon Potter
 */

#ifndef __FD_ENTRY_HH__
#define __FD_ENTRY_HH__

#include <memory>
#include <ostream>
#include <string>

#include "sim/serialize.hh"

class EmulatedDriver;

/**
 * Holds a single file descriptor mapping and that mapping's data for
 * processes running in syscall emulation mode.
 */
class FDEntry : public Serializable
{
  public:
    FDEntry(bool close_on_exec = false)
        : _closeOnExec(close_on_exec)
    { }

    virtual std::shared_ptr<FDEntry> clone() const = 0;

    bool getCOE() const { return _closeOnExec; }

    void setCOE(bool close_on_exec) { _closeOnExec = close_on_exec; }

    virtual void serialize(CheckpointOut &cp) const;
    virtual void unserialize(CheckpointIn &cp);

  protected:
    bool _closeOnExec;
};

/**
 * Extends the base class to include a host-backed file descriptor field
 * that records the integer used to represent the file descriptor on the host
 * and the file's flags.
 */
class HBFDEntry: public FDEntry
{
  public:
    HBFDEntry(int flags, int sim_fd, bool close_on_exec = false)
        : FDEntry(close_on_exec), _flags(flags), _simFD(sim_fd)
    { }

    HBFDEntry(HBFDEntry const& reg, bool close_on_exec = false)
        : FDEntry(close_on_exec), _flags(reg._flags), _simFD(reg._simFD)
    { }

    std::shared_ptr<FDEntry>
    clone() const override
    {
        return std::make_shared<HBFDEntry>(*this);
    }

    int getFlags() const { return _flags; }
    int getSimFD() const { return _simFD; }

    void setFlags(int flags) { _flags = flags; }
    void setSimFD(int sim_fd) { _simFD = sim_fd; }

  protected:
    int _flags;
    int _simFD;
};

/**
 * Holds file descriptors for host-backed files; host-backed files are
 * files which were opened on the physical machine where the simulation
 * is running (probably the thing on/under your desk). All regular files
 * are redirected to make it appear that the file descriptor assignment
 * starts at file descriptor '3' (not including stdin, stdout, stderr) and
 * then grows upward.
 */
class FileFDEntry: public HBFDEntry
{
  public:
    FileFDEntry(int sim_fd, int flags, std::string const& file_name,
                uint64_t file_offset, bool close_on_exec = false)
        : HBFDEntry(flags, sim_fd, close_on_exec),
          _fileName(file_name), _fileOffset(file_offset)
    { }

    FileFDEntry(FileFDEntry const& reg, bool close_on_exec = false)
        : HBFDEntry(reg._flags, reg._simFD, close_on_exec),
          _fileName(reg._fileName), _fileOffset(reg._fileOffset)
    { }

    std::shared_ptr<FDEntry>
    clone() const override
    {
        return std::make_shared<FileFDEntry>(*this);
    }

    std::string const& getFileName() const { return _fileName; }
    uint64_t getFileOffset() const { return _fileOffset; }

    void setFileName(std::string const& file_name) { _fileName = file_name; }
    void setFileOffset(uint64_t f_off) { _fileOffset = f_off; }

    void serialize(CheckpointOut &cp) const override;
    void unserialize(CheckpointIn &cp) override;

  private:
    std::string _fileName;
    uint64_t _fileOffset;
};

/**
 * Holds the metadata needed to maintain the mappings for file descriptors
 * allocated with the pipe() system calls and its variants.
 */
class PipeFDEntry: public HBFDEntry
{
  public:
    enum EndType {
        read = 0,
        write = 1
    };

    PipeFDEntry(int sim_fd, int flags, EndType pipe_end_type,
                bool close_on_exec = false)
        : HBFDEntry(flags, sim_fd, close_on_exec), _pipeReadSource(-1),
          _pipeEndType(pipe_end_type)
    { }

    PipeFDEntry(PipeFDEntry const& pipe, bool close_on_exec = false)
        : HBFDEntry(pipe._flags, pipe._simFD, close_on_exec),
          _pipeReadSource(pipe._pipeReadSource),
          _pipeEndType(pipe._pipeEndType)
    { }

    std::shared_ptr<FDEntry>
    clone() const override
    {
        return std::make_shared<PipeFDEntry>(*this);
    }

    EndType getEndType() const { return _pipeEndType; }
    int getPipeReadSource() const { return _pipeReadSource; }

    void setPipeReadSource(int tgt_fd) { _pipeReadSource = tgt_fd; }
    void setEndType(EndType type) { _pipeEndType = type; }

    void serialize(CheckpointOut &cp) const override;
    void unserialize(CheckpointIn &cp) override;

  private:
    int _pipeReadSource;
    EndType _pipeEndType;
};

/**
 * Holds file descriptors needed to simulate devices opened with pseudo
 * files (commonly with calls to ioctls).
 */
class DeviceFDEntry : public FDEntry
{
  public:
    DeviceFDEntry(EmulatedDriver *driver, std::string const& file_name,
                  bool close_on_exec = false)
        : FDEntry(close_on_exec), _driver(driver), _fileName(file_name)
    { }

    DeviceFDEntry(DeviceFDEntry const& dev, bool close_on_exec = false)
        : FDEntry(close_on_exec), _driver(dev._driver),
          _fileName(dev._fileName)
    { }

    std::shared_ptr<FDEntry>
    clone() const override
    {
        return std::make_shared<DeviceFDEntry>(*this);
    }

    EmulatedDriver *getDriver() const { return _driver; }
    std::string const& getFileName() const { return _fileName; }

    void serialize(CheckpointOut &cp) const override;
    void unserialize(CheckpointIn &cp) override;

  private:
    EmulatedDriver *_driver;
    std::string _fileName;
};

class SocketFDEntry: public HBFDEntry
{
  public:
    SocketFDEntry(int sim_fd, int domain, int type, int protocol,
                  bool close_on_exec = false)
        : HBFDEntry(0, sim_fd, close_on_exec),
          _domain(domain), _type(type), _protocol(protocol)
    { }

    SocketFDEntry(SocketFDEntry const& reg, bool close_on_exec = false)
        : HBFDEntry(reg._flags, reg._simFD, close_on_exec),
          _domain(reg._domain), _type(reg._type), _protocol(reg._protocol)
    { }

    std::shared_ptr<FDEntry>
    clone() const override
    {
        return std::make_shared<SocketFDEntry>(*this);
    }

    int _domain;
    int _type;
    int _protocol;
};

#endif // __FD_ENTRY_HH__