/* Copyright 2017 R. Thomas
 * Copyright 2017 Quarkslab
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <iostream>
#include <memory>
#include <fstream>
#include <algorithm>
#include <iterator>

#include <LIEF/MachO.hpp>

using namespace LIEF;

int main(int argc, char **argv) {
  std::cout << "MachO Instrumentation" << std::endl;
  if (argc != 3) {
    std::cerr << "Usage: " << argv[0] << " <Input Binary> <Output Binary>" << std::endl;
    return -1;
  }

  std::unique_ptr<LIEF::MachO::FatBinary> binaries{MachO::Parser::parse(argv[1])};
  MachO::Binary& binary = binaries->back();
  auto&& segments = binary.segments();
  auto&& itSegment  = std::find_if(
      std::begin(segments),
      std::end(segments),
      [] (const MachO::SegmentCommand& segment) {
        return segment.name() == "__TEXT";
      });

  if (itSegment != std::end(segments) and (*itSegment).sections().size() > 0) {
    MachO::SegmentCommand& segment = (*itSegment);
    MachO::Section& section        = *segment.sections();
    //segment.remove_all_sections();
    //segment.virtual_address(0xdeadbeef);
    //segment.name("foo");
    //section.name("bar");
  }

  // Modify symbol's name
  //auto symbols = binary->symbols();
  //for (MachO::Symbol& symbol : symbols) {
  //  symbol.name("toto");
  //}


  std::string segment_name = "__TEXT";
  std::vector<uint8_t> payload(0x30, 0x33);

  MachO::segment_command_64 segment_header;
  std::copy(std::begin(segment_name), std::end(segment_name), segment_header.segname);
  segment_header.segname[segment_name.size()] = 0;
  segment_header.vmaddr   = 0x200050000;
  segment_header.vmsize   = 0x2000;
  segment_header.fileoff  = binary.original_size();
  segment_header.filesize = payload.size();
  segment_header.maxprot  = 7;
  segment_header.initprot = 3;
  segment_header.nsects   = 0;
  segment_header.flags    = 0;
  segment_header.cmdsize  = sizeof(segment_header);
  segment_header.cmd      = static_cast<uint32_t>(MachO::LOAD_COMMAND_TYPES::LC_SEGMENT_64);

  std::unique_ptr<MachO::LoadCommand> segment{ new MachO::SegmentCommand{&segment_header}};
  segment->size(sizeof(segment_header));
    static_cast<MachO::SegmentCommand*>(segment.get())->content(payload);

  segment->data({
      reinterpret_cast<uint8_t*>(&segment_header),
      reinterpret_cast<uint8_t*>(&segment_header) + sizeof(segment_header)
      });
  //binary->insert_command(std::move(segment));
  binary.write(argv[2]);


  return 0;
}