/*
	PIP - Platform Independent Primitives
	Digest algorithms
	Ivan Pelipenko peri4ko@yandex.ru

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "pidigest_sha1_p.h"

template<typename T>
inline T rotate_u(T v, int bits) {
	return (v << bits) | (v >> ((sizeof(T) * 8) - bits));
}
template<typename T>
inline T shift_u(T v, int bits) {
	return v << bits;
}

PIByteArray SHA1::sha1(const PIByteArray & in) {
	constexpr int part_size = 64;
	constexpr int rounds    = 80;

	uint32_t h0             = 0x67452301u;
	uint32_t h1             = 0xEFCDAB89u;
	uint32_t h2             = 0x98BADCFEu;
	uint32_t h3             = 0x10325476u;
	uint32_t h4             = 0xC3D2E1F0u;

	uint64_t l              = in.size() * 8;
	PIByteArray msg         = in;
	int add_l               = (part_size - 8) - (msg.size() % (part_size));
	if (add_l <= 0) add_l += part_size;
	msg.append(0x80);
	for (int i = 1; i < add_l; ++i)
		msg.append(0x0);
	msg << piChangedEndian(l);
	int parts = msg.size_s() / part_size;
	// piCout << l << "add" << add_l << "=" << (in.size() + add_l + sizeof(l)) << msg.size() << parts;
	uint32_t w[80];
	for (int p = 0; p < parts; ++p) {
		uint32_t * mw = (uint32_t *)msg.data(p * part_size);
		for (int i = 0; i < 16; ++i)
			w[i] = piChangedEndian(mw[i]);
		for (int i = 16; i < 80; ++i) {
			w[i] = rotate_u((w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]), 1);
		}
		uint32_t a = h0;
		uint32_t b = h1;
		uint32_t c = h2;
		uint32_t d = h3;
		uint32_t e = h4;
		uint32_t f = 0;
		uint32_t k = 0;
		uint32_t t = 0;
		for (int i = 0; i < rounds; ++i) {
			if (i >= 0 && i <= 19) {
				f = (b & c) | ((~b) & d);
				k = 0x5A827999;
			} else if (i >= 20 && i <= 39) {
				f = b ^ c ^ d;
				k = 0x6ED9EBA1;
			} else if (i >= 40 && i <= 59) {
				f = (b & c) | (b & d) | (c & d);
				k = 0x8F1BBCDC;
			} else if (i >= 60 && i <= 79) {
				f = b ^ c ^ d;
				k = 0xCA62C1D6;
			}

			t = rotate_u(a, 5) + f + e + k + w[i];
			e = d;
			d = c;
			c = rotate_u(b, 30);
			b = a;
			a = t;
		}
		h0 += a;
		h1 += b;
		h2 += c;
		h3 += d;
		h4 += e;
	}
	PIByteArray ret;
	ret << piChangedEndian(h0) << piChangedEndian(h1) << piChangedEndian(h2) << piChangedEndian(h3) << piChangedEndian(h4);
	return ret;
}
